2016-07-26 6 views
4

Ich versuche, Go-Code als Postgresql gespeicherte Prozedur kompilieren und ausführen. Meine Motivation ist, weil postgresql excensions geschrieben in C und golang haben kann, kann als c-shared kompiliert werdenGolang Verfahrenssprache für Postgresql

So habe ich auf Dateien, 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))) 
} 

die CFLAGS und LDFLAGS i'we bekam von pg_config

und die Datei, wo ich die Funktion erstellen zu nennen, 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") 
} 

die gemeinsam genutzte Bibliothek erstellt mit: go build -buildmode=c-shared -o plgo.so plgo.go pl.go && sudo cp plgo.so /usr/lib/postgresql

die Funktion in postgresql mit erstellt:

CREATE OR REPLACE FUNCTION public.plgo_func(integer) 
    RETURNS text AS 
'$libdir/plgo', 'plgo_func' 
    LANGUAGE c IMMUTABLE STRICT 
    COST 1; 

aber wenn ich laufe: psql -U root -d meh -c "select plgo_func(0)"

der Server abstürzt mit:

server closed the connection unexpectedly 
    This probably means the server terminated abnormally 
    before or while processing the request. 
connection to server was lost 

EDIT: Ich habe erfolgreich erstellt eine Golang "Bibliothek" zum Erstellen gespeicherter Prozeduren und Trigger in Golang plgo :)

+1

C hat kein Konzept einer Go 'Schnittstelle {}'. Sie müssen einen C-Typ von Ihrer exportierten Funktion zurückgeben. (Wenn das nicht funktioniert, müssen Sie mehr Debugging-Informationen vom Server bekommen, um zu erfahren, warum es abgestürzt ist) – JimB

+0

Das hat nicht funktioniert, wie kann ich etwas Debugging/Log zu diesem Code hinzufügen? 'import" log "' und dann in Datei zu drucken funktioniert nicht ... – microo8

+1

Ich würde vermuten, dass Sie segfauling in der 'ret' -Funktion sind, da' cstring_to_text' einen '* text' zurückgibt, und Sie palloc nur die Größe des Zeigers, dann kopieren Sie die 'text'-Struktur in diese Position. Ich würde zuerst einen Machbarkeitsnachweis in C machen, um sicherzustellen, dass Sie das schaffen können, bevor Sie auf Go/cgo expandieren. – JimB

Antwort

3

Der Trick besteht darin, Aufrufkonventionen der Version 0 zu verwenden, da diese das Aufrufen einfacher C-Funktionen ermöglichen, ohne alle fancy Makros zu verwenden, die sie zu Aufruf von Version 1 hinzugefügt haben.

Sie benötigen auch eine einzelne C-Datei, um den Makro PG_MODULE_MAGIC aufzurufen.

Ich habe ein funktionierendes Beispielprojekt, das all dies tut, könnte am einfachsten sein, wenn Sie nur das als Ausgangspunkt kopiert haben.

Nicht sicher, dass Sie genau das tun können, was Sie wollen, aber es funktioniert tatsächlich für einige Anwendungsfälle.

https://github.com/dbudworth/gopgfuncs

Will auch die gleichen Sachen umreißen, die Sie Repo ...

mit Ihnen Funktionen und die CGO umfasst

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() { 
} 

Schritt 1

erstellen .go Datei sagt

Schritt 2

Erstellen Sie eine .c-Datei in demselben d Verzeichnis, das den Makro PG_MODULE_MAGIC von postgres.h aufruft, dies ist eine Voraussetzung für alle Erweiterungsbibliotheken. Go wird während des Kompilierens automatisch die C-Datei einschließen, es sind keine speziellen Anweisungen erforderlich.

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

PG_MODULE_MAGIC; 

Schritt 3

Erstellen Sie Ihre so Datei, hier Trick ist -buildmode = verwenden

c-shared
go build -buildmode=c-shared -o libMyMod.so 

Schritt 4

SQL ausführen Ihre Funktionen zu registrieren. Da die Registrierung einer Erweiterung absolute Pfade erfordert, überlasse ich das Modul lieber auf der Befehlszeile, um zu vermeiden, dass das Skript "install.sql" fest codierte Pfade enthält.

PGMOD=`pwd`/libMyMod.so 
psql $(DBNAME) --set=MOD=\'$(PGMOD)\' -f install.sql 

Install-Skript enthält eine dieser Anweisungen pro Funktion, die Sie exportieren.

Es ist absolut entscheidend, dass Sie die Eingabe-/Ausgabetypen richtig haben, da es nichts gibt, was das System tun kann, um sie zu überprüfen, und Sie am Ende auf Speicher, Speicher stampfen können. Sie können die Übersetzung von Pg-Typen zu C-Typen sehen here

create or replace function add_one(integer) 
    returns integer as :MOD,'AddOne' 
    LANGUAGE C STRICT; 
+0

Sieht gut aus, ich habe die Version 1 herum gearbeitet und jetzt gibt es ein kompliziertes Chaos auf meinem GitHub: https://github.com/microo8/plgo Es muss nicht alle CFLAGS und LDFLAGS Optionen geben? einfach c-shared erstellen? – microo8

+1

yeah, deshalb bin ich bei Version 0 geblieben. Ich wollte gespeicherte Procs in gehen. Schreiben Sie nicht einen Haufen C-Code, um alles miteinander zu verbinden. Sicherlich brauchst du nicht alle Flaggen, die meinen Ansatz verfolgen, vielleicht ist es für die v1-Makros erforderlich –