zoukankan      html  css  js  c++  java
  • golang ODBC 访问access数据库(问题解决之心理路程)

    最近项目需要,需要操作access,以前是用VC++ OLE访问,网络用ACE库,感觉很庞大。。。决定用go试试


    网上用的最多的就是这个https://github.com/weigj/go-odbc


    安装方式如下:

    ODBC database driver for Go
    
    Install:
    	cd $GOPATH/src
    	git clone git://github.com/weigj/go-odbc.git odbc
    	cd odbc
    	go 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 main
    
    import (
    	"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 Long
    	if 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 string
    		var SequenceNumber int
    		var ValueCode string
    		if err := row.Scan(&ID, &SequenceNumber, &ValueCode); err == nil {
    			fmt.Println(ID, SequenceNumber, ValueCode)
    		}
    	}
    	fmt.Printf("%s
    ", "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:1223
    	
    	goroutine 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可以凑合用。


    最后,你若用好,便是晴天。

  • 相关阅读:
    windows api学习笔记读写其他进程的内存
    WindowsApi学习笔记创建一个简单的窗口
    windows api学习笔记创建进程
    汇编语言基础教程加法指令
    windows api学习笔记使用定时器
    windows api学习笔记多线程
    C#中两个问号的双目运算符
    通过UDP的组播方式收发数据
    windows api学习笔记用临界区对象使线程同步
    工作流学习笔记ifElse活动;从工作流中取出返回值;计算器实例
  • 原文地址:https://www.cnblogs.com/james1207/p/3318168.html
Copyright © 2011-2022 走看看