GO 言語で PostgreSQL につなぐ
早速、実用に使いたいので、GO 言語(golang)で PostgreSQL につないでみたくなった。
課題:
1.GO 言語から、PostgreSQL につなぐ。
PostgreSQL bindings for golang
2.そのためには、GO 言語から、外部の C ライブラリを呼び出す必要がある。
C library(libpq) binding for golang.
いい加減だが、なんとかクリア。
追記:github においてみた。
I put PostgreSQL binding for golang to github. --> github:go-pg
$ git clone git://github.com/oibore/go-pg.git go-pg $ cd go-pg/src $ make $ ./runnner
まず、C で PostgrSQL のライブラリ libpq の Wrapper。
$ cat pg_wrapper.h #ifndef __PG_WRAPPER_H__ #define __PG_WRAPPER_H__ void *PgConnectDb(const char* conninfo); void PgFinish(void *conn); int PgStatus(void *conn); void *PgExec(void *conn, const char *command); void *PgGetResult(void *conn); int PgNFields(void *res); int PgNTuples(void *res); int PgGetIsNull(void *res, int row_number, int column_number); char *PgGetValue(void *res, int row_number, int column_number); #endif /* __PG_WRAPPER_H__ */ $ cat pg_wrapper.c #include#include "pg_wrapper.h" void *PgConnectDb(const char *conninfo) { PGconn *conn = PQconnectdb(conninfo); return conn; } void PgFinish(void *conn) { PQfinish(conn); } int PgStatus(void *conn) { return PQstatus(conn); } void *PgExec(void *conn, const char *command) { return PQexec(conn, command); } void *PgGetResult(void *conn) { return PQgetResult(conn); } int PgNFields(void *res) { return PQnfields(res); } int PgNTuples(void *res) { return PQntuples(res); } int PgGetIsNull(void *res, int row_number, int column_number) { return PQgetisnull(res, row_number, column_number); } char *PgGetValue(void *res, int row_number, int column_number) { return PQgetvalue(res, row_number, column_number); }
次に、GO の import する pkg(?)。
$ cat pg.go package pg // #include "pg_wrapper.h" import "C" import ( "unsafe"; ) func Connect(conninfo string) unsafe.Pointer { conn := C.PgConnectDb(C.CString(conninfo)); return conn; } func Close(conn unsafe.Pointer) { C.PgFinish(conn); } func Status(conn unsafe.Pointer) int { status := C.PgStatus(conn); return int(status); } func Exec(conn unsafe.Pointer, command string) unsafe.Pointer { res := C.PgExec(conn, C.CString(command)); return res; } func GetResult(conn unsafe.Pointer) unsafe.Pointer { return C.PgGetResult(conn); } func NFields(res unsafe.Pointer) int { return int(C.PgNFields(res)); } func NTuples(res unsafe.Pointer) int { return int(C.PgNTuples(res)); } func GetIsNull(res unsafe.Pointer, row_number int, column_number int) int { return int(C.PgGetIsNull(res, _C_int(row_number), _C_int(column_number))); } func GetValue(res unsafe.Pointer, row_number int, column_number int) string { value := C.GoString(C.PgGetValue(res, _C_int(row_number), _C_int(column_number))); return value; }
ほんで、本体となる go スクリプト。
$ cat runner.go package main import ( "fmt"; "pg"; ) func main() { conninfo := "dbname=testdb"; conn := pg.Connect(conninfo); status := pg.Status(conn); fmt.Printf("conninfo=%s, status=%d\n", conninfo, status); res := pg.Exec(conn, "select * from users;"); //res := pg.GetResult(conn); fileds := pg.NFields(res); fmt.Printf("fields = %d\n", fileds); value := pg.GetValue(res, 0, 0); fmt.Printf("value = %s\n", value); pg.Close(conn); }
それと、Makefile。これは i386, Linux 用なので、64な人とか、MacOS X などは適宜変更すること。
You need to change Makefile for $GOARCH and $GOOS.
$ Makefile include $(GOROOT)/src/Make.$(GOARCH) TARG=pg CGOFILES=pg.go CGO_LDFLAGS=pg_wrapper.o -lpq CLEANERFILES+=runner include $(GOROOT)/src/Make.pkg all: runner runner: pg_wrapper.o install runner.go $(GC) runner.go $(LD) -o $@ runner.$O pg_wrapper.o: pg_wrapper.c gcc -fPIC -O2 -o pg_wrapper.o -c pg_wrapper.c
あとは make すればよし。
[oibore@dural pg]$ ls Makefile pg.go pg_wrapper.c pg_wrapper.h runner.go work [oibore@dural pg]$ make cgo pg.go 8g -o _go_.8 pg.cgo1.go pg.cgo2.go 8c -FVw -I/home/oibore/go/src/pkg/runtime pg.cgo3.c rm -f _obj/pg.a gopack grc _obj/pg.a _go_.8 pg.cgo3.8 gcc -fPIC -O2 -o pg_wrapper.o -c pg_wrapper.c cp _obj/pg.a /home/oibore/go/pkg/linux_386/pg.a gcc -m32 -fPIC -O2 -o pg.cgo4.o -c pg.cgo4.c gcc -m32 -shared -lpthread -lm -o pg_pg.so pg.cgo4.o pg_wrapper.o -lpq cp pg_pg.so /home/oibore/go/pkg/linux_386/./pg_pg.so 8g runner.go 8l -o runner runner.8 rm pg.cgo4.o pg_pg.so [oibore@dural pg]$ ./runner fields = 2 value = foo
面倒なので、ファイル一式アップしておく。
追記:
github においてみた。--> github:go-pg
まだ、複数行とタプルを連続してとれない。
select して、0行目の0番目のカラムみたいなかんじ。
まずは各 DB 毎にドライバかいて、いずれは DBI みたいなのを作るのかな。
ややこしかったところ:
1) C の Wrap で、相互に変数をやり取りするとき、
String の場合、 C -> GO は C.CString()、 GO -> C は C.GoString() をつかう。
int の場合、C -> GO は int()でいけるが、 GO -> C は _C_int() とする必要がある。
2) C のライブラリを呼び出すのは
// include "pg_wrapper.h" import "C"
とか、やるらしい。
参考リンク
go で sqlite3
gmp.go
gotweet
libpq - C ライブラリ
CPAN Pg
海外から見に来る人が多いので、(下手な)英語を少し追加。