如何使用cgo构建Postgres扩展

时间:2017-01-04 05:27:15

标签: 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"

Makefile

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

我不知道这是否有用。这里发生了很多黑魔法。

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

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

1 个答案:

答案 0 :(得分:3)

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

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

开头
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侧外加这些符号。确保导出的Go方法驻留在main之外的包中。

package test

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

package main

extern int Merge(int);

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