2016-07-26 37 views
4

我想編譯並運行go代碼作爲Postgresql存儲過程。 我的動機是因爲PostgreSQL可以用C語言編寫golang excensions可以被編譯爲c-共享Golang過程語言Postgresql

所以我要文件,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))) 
} 

CFLAGSLDFLAGS i'we了從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 :)

+1

C沒有任何Go接口{}的概念。您需要從導出的函數中返回一個C類型。 (如果這不起作用,您需要從服務器獲得更多調試信息,以瞭解它爲什麼崩潰) – JimB

+0

這沒有奏效,我該如何在該代碼中添加一些調試/日誌? '導入「日誌」'而不是打印到文件不起作用... – microo8

+1

我想你是'ret'函數中的segfaulting,因爲'cstring_to_text'返回一個'* text',而你只有palloc該指針的大小,然後將'text'結構複製到該位置。我會首先在C語言中做一個概念驗證,以確保在擴展到Go/cgo之前可以完成這個工作。 – JimB

回答

3

訣竅是使用0版本的調用約定,因爲那些允許調用簡單的C函數而不使用它們添加到版本1調用中的所有花哨的宏。

您還需要一個C文件來調用PG_MODULE_MAGIC宏。

我有一個可以完成所有工作的工作示例項目,如果您只是將其複製爲起點,那麼可能是最簡單的。

不確定它會讓你做到你想要的東西,但它的確適用於某些用例。

https://github.com/dbudworth/gopgfuncs

也將概述回購告訴你同樣的東西...

步驟1

創建你函數。去文件和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

在同一個d中創建一個.c文件從postgres.h調用PG_MODULE_MAGIC宏的irectory,這是所有擴展庫的要求。 Go會在編譯時自動包含C文件,不需要特別指示。

#include "postgres.h" 
#include "fmgr.h" 

PG_MODULE_MAGIC; 

步驟3

建立您因此文件,誘騙這裏是使用-buildmode = C-共享

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; 
+0

看起來不錯,我工作過版本1,現在在我的github上有一個複雜的混亂:https://github.com/microo8/plgo 有沒有所有的CFLAGS和LDFLAGS選項?只是建立共享? – microo8

+1

是的,這就是爲什麼我堅持版本0.我想存儲過程中去。不要寫一堆C代碼來將它們連接在一起。當然,你不需要所有的標誌都採取我的方法,也許這是V1宏所必需的 –