我正在尝试将go代码作为Postgresql存储过程运行。
我的动机是因为postgresql可以使用C编写补偿,而golang可以编译为c共享
所以我必须归档,pl.go:
package main
/*
#cgo CFLAGS: -Wall -Wpointer-arith -Wno-declaration-after-statement -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -march=x86-64 -mtune=generic -O2 -pipe -fstack-protector-strong -fpic -I. -I./ -I/usr/include/postgresql/server -I/usr/include/postgresql/internal -D_FORTIFY_SOURCE=2 -D_GNU_SOURCE -I/usr/include/libxml2
#cgo LDFLAGS: -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -march=x86-64 -mtune=generic -O2 -pipe -fstack-protector-strong -fpic -L/usr/lib -Wl,-O1,--sort-common,--as-needed,-z,relro -Wl,--as-needed -Wl,-rpath,'/usr/lib',--enable-new-dtags -shared
#include "postgres.h"
#include "fmgr.h"
#include "utils/builtins.h"
#ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC;
#endif
//the return value must be allocated trough palloc
void* ret(void *val, uint64 *size) {
void *retDatum = palloc(*size);
memcpy(retDatum, val, *size);
return retDatum;
}
PG_FUNCTION_INFO_V1(plgo_func);
*/
import "C"
import "unsafe"
func main() {}
//PGVal returns the Postgresql C type from Golang type (currently implements just stringtotext)
func PGVal(val interface{}) (ret interface{}) {
var size uintptr
switch v := val.(type) {
case string:
ret = C.cstring_to_text(C.CString(v))
size = unsafe.Sizeof(ret)
default:
ret = val
size = unsafe.Sizeof(ret)
}
return C.ret(ret, (*C.uint64)(unsafe.Pointer(size)))
}
我从
CFLAGS
获得的LDFLAGS
和pg_config
以及我创建要调用的函数的文件plgo.go:
package main
/*
#include "postgres.h"
#include "fmgr.h"
#include "utils/builtins.h"
*/
import "C"
//export plgo_func
func plgo_func(fcinfo *C.FunctionCallInfoData) interface{} {
return PGVal("meh")
}
共享库的创建方式为:
go build -buildmode=c-shared -o plgo.so plgo.go pl.go && sudo cp plgo.so /usr/lib/postgresql
postgresql中的函数是使用以下命令创建的:
CREATE OR REPLACE FUNCTION public.plgo_func(integer)
RETURNS text AS
'$libdir/plgo', 'plgo_func'
LANGUAGE c IMMUTABLE STRICT
COST 1;
但是当我运行时:
psql -U root -d meh -c "select plgo_func(0)"
服务器崩溃:
server closed the connection unexpectedly
This probably means the server terminated abnormally
before or while processing the request.
connection to server was lost
编辑:我已经成功创建了golang“库”,用于在golang plgo中创建存储过程和触发器:)
最佳答案
诀窍是使用版本0调用约定,因为那些约定允许调用简单的C函数,而无需使用它们添加到版本1调用中的所有奇特宏。
您还需要一个C文件来调用PG_MODULE_MAGIC宏。
我有一个工作的示例项目可以完成所有这一切,如果您只是将其复制为起点,那可能是最简单的。
不确定是否可以让您完全按照自己的意愿做,但确实可以在某些用例中使用。
https://github.com/dbudworth/gopgfuncs
还将概述 repo 告诉您的内容...
步骤1
使用您的函数创建一个.go文件,CGO包括
package main
//#include "postgres.h"
//#include "fmgr.h"
//#include <string.h>
import "C"
import (
"log"
"sync/atomic"
)
// Functions are scoped to the db session
var counter int64
//Inc atomically increments a session local counter by a given delta
//export Inc
func Inc(delta C.int64) C.int64 {
log.Printf("Inc(%v) called", delta)
return C.int64(atomic.AddInt64(&counter, int64(delta)))
}
//AddOne adds one to the given arg and retuns it
//export AddOne
func AddOne(i C.int) C.int {
log.Printf("AddOne(%v) called", i)
return i + 1
}
func main() {
}
第2步
在从postgres.h调用PG_MODULE_MAGIC宏的同一目录中创建一个.c文件,这是所有扩展库的要求。
Go会在编译时自动包含C文件,不需要特殊说明。
#include "postgres.h"
#include "fmgr.h"
PG_MODULE_MAGIC;
第三步
构建您的so文件,这里的技巧是使用-buildmode = c-shared
go build -buildmode=c-shared -o libMyMod.so
第4步
执行SQL来注册您的函数。由于注册扩展名需要绝对路径,因此我更喜欢在命令行上传递模块,以避免使“install.sql”脚本中包含硬编码的路径。
PGMOD=`pwd`/libMyMod.so
psql $(DBNAME) --set=MOD=\'$(PGMOD)\' -f install.sql
安装脚本在您导出的每个函数中包含以下语句之一。
正确设置输入/输出类型至关重要,因为系统无法执行任何操作来验证它们,并且最终可能会占用大量内存,存储空间或其他资源。您可以看到pg类型到c类型here的转换
create or replace function add_one(integer)
returns integer as :MOD,'AddOne'
LANGUAGE C STRICT;