這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
最近項目需要,需要操作access,以前是用VC++ OLE訪問,網路用ACE庫,感覺很龐大。。。決定用go試試
網上用的最多的就是這個https://github.com/weigj/go-odbc
安裝方式如下:
ODBC database driver for GoInstall:cd $GOPATH/srcgit clone git://github.com/weigj/go-odbc.git odbccd odbcgo install
測試時碰到好多坑。。。。。
第1次當運行go install時,
坑爹1:發現找不到gcc,哦, 推測cgo去連結odbc32的dll,需要gcc編譯環境,幸虧哥搞cocos2d-x時電腦上已經裝了龐大的cygwin,於是啟動Cygwin Terminal,跳轉到odbc目錄,
第2次當運行go install時,
坑爹2:提示access limit。。可是在console裡直接運行gcc -v是存在的,感覺是gcc這個是linux的符號連結,推測go install時可能沒用利用cygwin的環境設定,有點衝突,於是刪掉gcc這個符號link,把真正的gcc-4.exe改名為gcc.exe,
第3次當運行go install時,
坑爹3:提示找不到ODBC的函數符號,你妹,不得不fuck source code了,讀源碼odbc.go,發現匯入有個macro定義,
#ifdef __MINGW32__
#include <windef.h>
#else
typedef void* HANDLE;
#endif
原來odbc.go編譯依賴MINGW環境,於是下載mingw,安裝完後,把mingw的bin目錄加到系統path裡,編譯成功。
go run 下面的例子代碼,
package mainimport ("database/sql""fmt"_ "odbc/driver")func main() {conn, err := sql.Open("odbc", "driver={Microsoft Access Driver (*.mdb)};dbq=d:\\test.mdb")if err != nil {fmt.Println("Connecting Error")return}defer conn.Close()stmt, err := conn.Prepare("select * from test") //ALTER TABLE tb ALTER COLUMN aa Longif err != nil {fmt.Println("Query Error")return}defer stmt.Close()row, err := stmt.Query()if err != nil {fmt.Print(err)fmt.Println("Query Error")return}defer row.Close()for row.Next() {var ID stringvar SequenceNumber intvar ValueCode stringif err := row.Scan(&ID, &SequenceNumber, &ValueCode); err == nil {fmt.Println(ID, SequenceNumber, ValueCode)}}fmt.Printf("%s\n", "finish")return}
坑爹4:居然提示fatal error: malloc/free - deadlock[signal 0xc0000005 code=0x1 addr=0x2f0 pc=0x419258]goroutine 1 [syscall]:[fp=0x10815fc] return()C:/Users/ADMINI~1/AppData/Local/Temp/2/bindist465310315/go/src/pkg/runtime/asm_386.s:472[fp=0x1081624] runtime.cgocall(0x470a23, 0x1081630)C:/Users/ADMINI~1/AppData/Local/Temp/2/bindist465310315/go/src/pkg/runtime/cgocall.c:162 +0x10a[fp=0x1081630] odbc._Cfunc_SQLFreeHandle(0x400003, 0xb115e8, 0x1081650)C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/go-build619492171/odbc/_obj/_cgo_defun.c:144 +0x31[fp=0x1081640] odbc.(*Statement).free(0x2f860140)odbc.go:745 +0x32[fp=0x1081648] odbc.(*Statement).Close(0x2f860140)odbc.go:752 +0x28[fp=0x1081650] odbc/driver.(*stmt).Close(0x2f860148, 0x2f8601c8, 0x405a2b)E:/go/src/odbc/driver/sql.go:107 +0x2a[fp=0x108168c] database/sql.(*DB).noteUnusedDriverStatement(0x2f884300, 0x2f8821e0, 0x2f882240, 0x2f860148)C:/Users/ADMINI~1/AppData/Local/Temp/2/bindist465310315/go/src/pkg/database/sql/sql.go:554 +0x17a[fp=0x10816d0] database/sql.(*Stmt).finalClose(0x2f8862d0, 0x2f8601b8, 0x0)C:/Users/ADMINI~1/AppData/Local/Temp/2/bindist465310315/go/src/pkg/database/sql/sql.go:1264 +0x84[fp=0x10816e0] database/sql.func·002(0x2f884310, 0x2f894720)C:/Users/ADMINI~1/AppData/Local/Temp/2/bindist465310315/go/src/pkg/database/sql/sql.go:372 +0x2c[fp=0x1081700] database/sql.(*DB).removeDep(0x2f884300, 0x2f894720, 0x2f8862d0, 0x49c740, 0x2f8862d0, ...)C:/Users/ADMINI~1/AppData/Local/Temp/2/bindist465310315/go/src/pkg/database/sql/sql.go:351 +0x78[fp=0x1081720] database/sql.(*Stmt).Close(0x2f8862d0, 0x0, 0x0)C:/Users/ADMINI~1/AppData/Local/Temp/2/bindist465310315/go/src/pkg/database/sql/sql.go:1259 +0x117[fp=0x10817c4] main.main()E:/go/src/testaccessdb/testdb.go:12 +0xbf[fp=0x10817dc] runtime.main()C:/Users/ADMINI~1/AppData/Local/Temp/2/bindist465310315/go/src/pkg/runtime/proc.c:182 +0x8e[fp=0x10817e0] runtime.goexit()C:/Users/ADMINI~1/AppData/Local/Temp/2/bindist465310315/go/src/pkg/runtime/proc.c:1223goroutine 2 [syscall]:exit status 2
這鬱悶了,google了半天也沒有解決方案,發現golang issue裡有這個問題,可是那貨說最新的go version已經解決了,可哥就是最新的版本啊,順手甚至提交這個bug到golang,決定放棄了。。。。。
可是哥的鑽研精神此刻閃閃發光,照亮我的前程了,反覆N遍,逐漸刪除測試代碼,最後發現
stmt.Query()
這個函數刪掉就pass了!
於是仔細讀上面的堆棧垃圾資訊發現問題可能是free 控制代碼odbc._Cfunc_SQLFreeHandle多次導致,懷疑是這個github.com/weigj/go-odbc.git 庫內建的bug,不是go的問題,於是使用已經廣而知之神器武功套路log大法,在free函數開始和結束處 加log, go run again發現free()函數果然被調用了兩次。。。
於是哥修改
func (stmt *Statement) free() {
C.SQLFreeHandle(C.SQL_HANDLE_STMT, stmt.handle)
}
為
func (stmt *Statement) free() {
if stmt.handle != nil {
C.SQLFreeHandle(C.SQL_HANDLE_STMT, stmt.handle)
stmt.handle = nil
}
}
預期中的測試通過。。。錯誤提示沒有了,只有閃閃發光的finish。。。
善後:尼瑪,發現這作者兩年沒更新,而網上的程式碼片段都指向這個庫。。。於是哥提交了一份修複bug的完整代碼,請參考https://github.com/philsong/golang_samples
問題解決了,感覺這個庫不是很成熟,不過寫tool utility可以湊合用。