postgresql - 如何使用 cgo 构建 Postgres 扩展

标签 postgresql gcc go makefile cgo

这是我现在正在做的,

.
├── helloworld--1.0.sql
├── helloworld.control
├── helloworld.go
└── Makefile

helloworld.go :

package helloworld

/*
#cgo LDFLAGS: -rdynamic
#include "postgres.h"
#include "fmgr.h"
#include "utils/builtins.h"

PG_MODULE_MAGIC;

PG_FUNCTION_INFO_V1(helloworld);
PG_FUNCTION_INFO_V1(hello_text_arg);
PG_FUNCTION_INFO_V1(hello_ereport);

Datum
hello_world(PG_FUNCTION_ARGS)
{
    PG_RETURN_TEXT_P(cstring_to_text("Hello, World!"));
}

Datum
hello_text_arg(PG_FUNCTION_ARGS)
{
    text *hello     = cstring_to_text("Hello, ");
    int32 hello_sz  = VARSIZE(hello) - VARHDRSZ;

    text *name      = PG_GETARG_TEXT_P(0);
    int32 name_sz   = VARSIZE(name) - VARHDRSZ;

    text *tail      = cstring_to_text("!");
    int32 tail_sz   = VARSIZE(tail) - VARHDRSZ;

    int32 out_sz    = hello_sz + name_sz + tail_sz + VARHDRSZ;
    text *out       = (text *) palloc(out_sz);

    SET_VARSIZE(out, out_sz);

    memcpy(VARDATA(out), VARDATA(hello), hello_sz);
    memcpy(VARDATA(out) + hello_sz, VARDATA(name), name_sz);
    memcpy(VARDATA(out) + hello_sz + name_sz, VARDATA(tail), tail_sz);

    PG_RETURN_TEXT_P(out);
}

Datum
hello_ereport(PG_FUNCTION_ARGS)
{
    ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("null value not allowed")));

    PG_RETURN_VOID();
}
*/
import "C"

生成文件:

MODULES = helloworld

EXTENSION = helloworld
DATA = helloworld--1.0.sql
PGFILEDESC = "helloworld - example extension for postgresql"

REGRESS = helloworld

PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
INCLUDEDIR = $(shell $(PG_CONFIG) --includedir-server)
include $(PGXS)

helloworld.so:
    CGO_CFLAGS="-rdynamic -I$(INCLUDEDIR)" CGO_LDFLAGS="-rdynamic $(LDFLAGS)" go build -v -buildmode=c-shared -o helloworld.so .

它在制作时产生了这些错误:

/tmp/go-build019341122/github.com/amosbird/rpctest/helloworld/_obj/helloworld.cgo2.o: In function `hello_world':
helloworld.cgo2.c:(.text+0x48): undefined reference to `cstring_to_text'
/tmp/go-build019341122/github.com/amosbird/rpctest/helloworld/_obj/helloworld.cgo2.o: In function `hello_text_arg':
helloworld.cgo2.c:(.text+0x63): undefined reference to `cstring_to_text'
helloworld.cgo2.c:(.text+0x86): undefined reference to `pg_detoast_datum'
helloworld.cgo2.c:(.text+0xa5): undefined reference to `cstring_to_text'
helloworld.cgo2.c:(.text+0xd7): undefined reference to `palloc'
/tmp/go-build019341122/github.com/amosbird/rpctest/helloworld/_obj/helloworld.cgo2.o: In function `hello_ereport':
helloworld.cgo2.c:(.text+0x1a8): undefined reference to `errstart'
helloworld.cgo2.c:(.text+0x1bd): undefined reference to `errmsg'
helloworld.cgo2.c:(.text+0x1c9): undefined reference to `errcode'
helloworld.cgo2.c:(.text+0x1d7): undefined reference to `errfinish'
collect2: ld returned 1 exit status
make: *** [helloworld.so] Error 2

我不知道这是否可行。这里发生了太多的黑魔法。

所以主要问题是,可用于在 cgo 中构建 Postgres 扩展的正确 Makefile 是什么?

这些错误的一个具体问题是,我能做些什么来推迟 cgo 链接过程中的那些符号解析?

最佳答案

好吧,我花了一整天的时间,找到了一个可行的解决方案。

我们需要让扩展的主要源文件以:

package main  // make sure to use main package

/*
#cgo CFLAGS: -I/path/to/postgres/include/server
#cgo LDFLAGS: -Wl,-unresolved-symbols=ignore-all

使用go build -o myext.so -buildmode=c-shared myext.go 生成myext.so

如果C端需要一些Go方法,我们应该在方法声明上方添加//export methodname。这将生成没有包名称前缀的符号。然后我们就可以在C端extern这些符号了。确保导出的 Go 方法位于 main 以外的包中。

打包测试:

//export Merge
func Merge(cint C.int) C.int ...

主要包:

extern int Merge(int);

import "./test"
var _ = test.Somevar  // dumb placeholder to fake use package test.

关于postgresql - 如何使用 cgo 构建 Postgres 扩展,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41456777/

相关文章:

go - 如何使用反射来设置嵌套的结构域值

xml - golang如何解码带有特殊字符的xml

php - 如果密码包含空格,PDO 无法连接

postgresql - 提高 PostgreSQL 中 GROUP BY ... HAVING COUNT(...) > 1 的性能

postgresql - 在 Postgresql 上以编程方式生成 DDL

c - 用于扩展除法/乘法的 gcc intrinsic

c - 将数据放入 .text 时会出现汇编器警告和 gcc 警告

c - 使用多维数组作为输入构建 Postgresql C 函数

gcc - 带有自定义 gcc 安装的 LIBRARY_PATH 的优先级

xml - 获取原始元素表示,包括开始和结束标签