766 lines
20 KiB
Go
766 lines
20 KiB
Go
|
|
// Copyright (c) 2011, Wei guangjing <vcc.163@gmail.com>. All rights reserved.
|
||
|
|
// Use of this source code is governed by a BSD-style
|
||
|
|
// license that can be found in the LICENSE file.
|
||
|
|
|
||
|
|
package odbc
|
||
|
|
|
||
|
|
/*
|
||
|
|
#cgo darwin LDFLAGS: -lodbc
|
||
|
|
#cgo freebsd LDFLAGS: -lodbc
|
||
|
|
#cgo linux LDFLAGS: -lodbc
|
||
|
|
#cgo windows LDFLAGS: -lodbc32
|
||
|
|
|
||
|
|
#include <stdio.h>
|
||
|
|
#include <stdlib.h>
|
||
|
|
|
||
|
|
#ifdef __MINGW32__
|
||
|
|
#include <windef.h>
|
||
|
|
#else
|
||
|
|
typedef void* HANDLE;
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#include <sql.h>
|
||
|
|
#include <sqlext.h>
|
||
|
|
#include <sqltypes.h>
|
||
|
|
|
||
|
|
SQLRETURN _SQLColAttribute (
|
||
|
|
SQLHSTMT StatementHandle,
|
||
|
|
SQLUSMALLINT ColumnNumber,
|
||
|
|
SQLUSMALLINT FieldIdentifier,
|
||
|
|
SQLPOINTER CharacterAttributePtr,
|
||
|
|
SQLSMALLINT BufferLength,
|
||
|
|
SQLSMALLINT * StringLengthPtr,
|
||
|
|
void * NumericAttributePtr) {
|
||
|
|
return SQLColAttribute(StatementHandle,
|
||
|
|
ColumnNumber,
|
||
|
|
FieldIdentifier,
|
||
|
|
CharacterAttributePtr,
|
||
|
|
BufferLength,
|
||
|
|
StringLengthPtr,
|
||
|
|
NumericAttributePtr);
|
||
|
|
}
|
||
|
|
|
||
|
|
*/
|
||
|
|
import "C"
|
||
|
|
import (
|
||
|
|
"database/sql/driver"
|
||
|
|
"fmt"
|
||
|
|
"reflect"
|
||
|
|
"time"
|
||
|
|
"unsafe"
|
||
|
|
)
|
||
|
|
|
||
|
|
const (
|
||
|
|
BUFFER_SIZE = 10 * 1024
|
||
|
|
INFO_BUFFER_LEN = 256
|
||
|
|
)
|
||
|
|
|
||
|
|
var (
|
||
|
|
Genv C.SQLHANDLE
|
||
|
|
)
|
||
|
|
|
||
|
|
type Connection struct {
|
||
|
|
Dbc C.SQLHANDLE
|
||
|
|
connected bool
|
||
|
|
}
|
||
|
|
|
||
|
|
type Statement struct {
|
||
|
|
executed bool
|
||
|
|
prepared bool
|
||
|
|
scrollable bool
|
||
|
|
|
||
|
|
handle C.SQLHANDLE
|
||
|
|
}
|
||
|
|
|
||
|
|
type ODBCError struct {
|
||
|
|
SQLState string
|
||
|
|
NativeError int
|
||
|
|
ErrorMessage string
|
||
|
|
}
|
||
|
|
|
||
|
|
func (e *ODBCError) Error() string {
|
||
|
|
return e.String()
|
||
|
|
}
|
||
|
|
|
||
|
|
func (e *ODBCError) String() string {
|
||
|
|
if e != nil {
|
||
|
|
return e.SQLState + " " + e.ErrorMessage
|
||
|
|
}
|
||
|
|
return ""
|
||
|
|
}
|
||
|
|
|
||
|
|
func initEnv() (err *ODBCError) {
|
||
|
|
ret := C.SQLAllocHandle(C.SQL_HANDLE_ENV, nil, &Genv)
|
||
|
|
if !Success(ret) {
|
||
|
|
err := FormatError(C.SQL_HANDLE_ENV, Genv)
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
ret = C.SQLSetEnvAttr(C.SQLHENV(Genv), C.SQL_ATTR_ODBC_VERSION, C.SQLPOINTER(unsafe.Pointer(uintptr(C.SQL_OV_ODBC3))), C.SQLINTEGER(0))
|
||
|
|
if !Success(ret) {
|
||
|
|
err := FormatError(C.SQL_HANDLE_ENV, Genv)
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func Connect(dsn string, params ...interface{}) (conn *Connection, err *ODBCError) {
|
||
|
|
var h C.SQLHANDLE
|
||
|
|
ret := C.SQLAllocHandle(C.SQL_HANDLE_DBC, Genv, &h)
|
||
|
|
if !Success(ret) {
|
||
|
|
err := FormatError(C.SQL_HANDLE_DBC, h)
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
var stringLength2 C.SQLSMALLINT
|
||
|
|
outBuf := make([]byte, BUFFER_SIZE*2)
|
||
|
|
outConnectionString := (*C.SQLWCHAR)(unsafe.Pointer(&outBuf[0]))
|
||
|
|
|
||
|
|
ret = C.SQLDriverConnectW(C.SQLHDBC(h),
|
||
|
|
C.SQLHWND(unsafe.Pointer(uintptr(0))),
|
||
|
|
(*C.SQLWCHAR)(unsafe.Pointer(StringToUTF16Ptr(dsn))),
|
||
|
|
C.SQL_NTS,
|
||
|
|
outConnectionString,
|
||
|
|
BUFFER_SIZE,
|
||
|
|
&stringLength2,
|
||
|
|
C.SQL_DRIVER_NOPROMPT)
|
||
|
|
|
||
|
|
if !Success(ret) {
|
||
|
|
err := FormatError(C.SQL_HANDLE_DBC, h)
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
return &Connection{Dbc: h, connected: true}, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (conn *Connection) ExecDirect(sql string) (stmt *Statement, err *ODBCError) {
|
||
|
|
if stmt, err = conn.newStmt(); err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
wsql := StringToUTF16Ptr(sql)
|
||
|
|
ret := C.SQLExecDirectW(C.SQLHSTMT(stmt.handle), (*C.SQLWCHAR)(unsafe.Pointer(wsql)), C.SQL_NTS)
|
||
|
|
if !Success(ret) {
|
||
|
|
err := FormatError(C.SQL_HANDLE_STMT, stmt.handle)
|
||
|
|
stmt.Close()
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
stmt.executed = true
|
||
|
|
return stmt, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (conn *Connection) newStmt() (*Statement, *ODBCError) {
|
||
|
|
stmt := &Statement{}
|
||
|
|
|
||
|
|
ret := C.SQLAllocHandle(C.SQL_HANDLE_STMT, conn.Dbc, &stmt.handle)
|
||
|
|
if !Success(ret) {
|
||
|
|
err := FormatError(C.SQL_HANDLE_DBC, conn.Dbc)
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
return stmt, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (conn *Connection) Prepare(sql string, params ...interface{}) (*Statement, *ODBCError) {
|
||
|
|
wsql := StringToUTF16Ptr(sql)
|
||
|
|
stmt, err := conn.newStmt()
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
ret := C.SQLPrepareW(C.SQLHSTMT(stmt.handle), (*C.SQLWCHAR)(unsafe.Pointer(wsql)), C.SQLINTEGER(len(sql)))
|
||
|
|
if !Success(ret) {
|
||
|
|
err := FormatError(C.SQL_HANDLE_STMT, stmt.handle)
|
||
|
|
stmt.Close()
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
stmt.prepared = true
|
||
|
|
return stmt, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (conn *Connection) Commit() (err *ODBCError) {
|
||
|
|
ret := C.SQLEndTran(C.SQL_HANDLE_DBC, conn.Dbc, C.SQL_COMMIT)
|
||
|
|
if !Success(ret) {
|
||
|
|
err = FormatError(C.SQL_HANDLE_DBC, conn.Dbc)
|
||
|
|
}
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
func (conn *Connection) AutoCommit(b bool) (err *ODBCError) {
|
||
|
|
var n C.int
|
||
|
|
if b {
|
||
|
|
n = C.SQL_AUTOCOMMIT_ON
|
||
|
|
} else {
|
||
|
|
n = C.SQL_AUTOCOMMIT_OFF
|
||
|
|
}
|
||
|
|
ret := C.SQLSetConnectAttr(C.SQLHDBC(conn.Dbc), C.SQL_ATTR_AUTOCOMMIT, C.SQLPOINTER(unsafe.Pointer(uintptr(n))), C.SQL_IS_UINTEGER)
|
||
|
|
if !Success(ret) {
|
||
|
|
err = FormatError(C.SQL_HANDLE_DBC, conn.Dbc)
|
||
|
|
}
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
func (conn *Connection) BeginTransaction() (err *ODBCError) {
|
||
|
|
ret := C.SQLSetConnectAttr(C.SQLHDBC(conn.Dbc), C.SQL_ATTR_AUTOCOMMIT, C.SQLPOINTER(unsafe.Pointer(uintptr(C.SQL_AUTOCOMMIT_OFF))), C.SQL_IS_UINTEGER)
|
||
|
|
if !Success(ret) {
|
||
|
|
err = FormatError(C.SQL_HANDLE_DBC, conn.Dbc)
|
||
|
|
}
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
func (conn *Connection) Rollback() (err *ODBCError) {
|
||
|
|
ret := C.SQLEndTran(C.SQL_HANDLE_DBC, conn.Dbc, C.SQL_ROLLBACK)
|
||
|
|
if !Success(ret) {
|
||
|
|
err = FormatError(C.SQL_HANDLE_DBC, conn.Dbc)
|
||
|
|
}
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
func (conn *Connection) ServerInfo() (string, string, string, *ODBCError) {
|
||
|
|
var info_len C.SQLSMALLINT
|
||
|
|
p := make([]byte, INFO_BUFFER_LEN)
|
||
|
|
ret := C.SQLGetInfo(C.SQLHDBC(conn.Dbc), C.SQL_DATABASE_NAME, C.SQLPOINTER(unsafe.Pointer(&p[0])), INFO_BUFFER_LEN, &info_len)
|
||
|
|
if !Success(ret) {
|
||
|
|
err := FormatError(C.SQL_HANDLE_DBC, conn.Dbc)
|
||
|
|
return "", "", "", err
|
||
|
|
}
|
||
|
|
db := string(p[0:info_len])
|
||
|
|
ret = C.SQLGetInfo(C.SQLHDBC(conn.Dbc), C.SQL_DBMS_VER, C.SQLPOINTER(unsafe.Pointer(&p[0])), INFO_BUFFER_LEN, &info_len)
|
||
|
|
if !Success(ret) {
|
||
|
|
err := FormatError(C.SQL_HANDLE_DBC, conn.Dbc)
|
||
|
|
return db, "", "", err
|
||
|
|
}
|
||
|
|
ver := string(p[0:info_len])
|
||
|
|
ret = C.SQLGetInfo(C.SQLHDBC(conn.Dbc), C.SQL_SERVER_NAME, C.SQLPOINTER(unsafe.Pointer(&p[0])), INFO_BUFFER_LEN, &info_len)
|
||
|
|
if !Success(ret) {
|
||
|
|
err := FormatError(C.SQL_HANDLE_DBC, conn.Dbc)
|
||
|
|
return db, ver, "", err
|
||
|
|
}
|
||
|
|
server := string(p[0:info_len])
|
||
|
|
return db, ver, server, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (conn *Connection) ClientInfo() (string, string, string, *ODBCError) {
|
||
|
|
var info_len C.SQLSMALLINT
|
||
|
|
p := make([]byte, INFO_BUFFER_LEN)
|
||
|
|
ret := C.SQLGetInfo(C.SQLHDBC(conn.Dbc), C.SQL_DRIVER_NAME, C.SQLPOINTER(unsafe.Pointer(&p[0])), INFO_BUFFER_LEN, &info_len)
|
||
|
|
if !Success(ret) {
|
||
|
|
err := FormatError(C.SQL_HANDLE_DBC, conn.Dbc)
|
||
|
|
return "", "", "", err
|
||
|
|
}
|
||
|
|
drv_name := string(p[0:info_len])
|
||
|
|
ret = C.SQLGetInfo(C.SQLHDBC(conn.Dbc), C.SQL_DRIVER_ODBC_VER, C.SQLPOINTER(unsafe.Pointer(&p[0])), INFO_BUFFER_LEN, &info_len)
|
||
|
|
if !Success(ret) {
|
||
|
|
err := FormatError(C.SQL_HANDLE_DBC, conn.Dbc)
|
||
|
|
return "", "", "", err
|
||
|
|
}
|
||
|
|
drv_odbc_ver := string(p[0:info_len])
|
||
|
|
ret = C.SQLGetInfo(C.SQLHDBC(conn.Dbc), C.SQL_DRIVER_VER, C.SQLPOINTER(unsafe.Pointer(&p[0])), INFO_BUFFER_LEN, &info_len)
|
||
|
|
if !Success(ret) {
|
||
|
|
err := FormatError(C.SQL_HANDLE_DBC, conn.Dbc)
|
||
|
|
return "", "", "", err
|
||
|
|
}
|
||
|
|
drv_ver := string(p[0:info_len])
|
||
|
|
return drv_name, drv_odbc_ver, drv_ver, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (conn *Connection) Close() *ODBCError {
|
||
|
|
if conn.connected {
|
||
|
|
ret := C.SQLDisconnect(C.SQLHDBC(conn.Dbc))
|
||
|
|
if !Success(ret) {
|
||
|
|
err := FormatError(C.SQL_HANDLE_DBC, conn.Dbc)
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
ret = C.SQLFreeHandle(C.SQL_HANDLE_DBC, conn.Dbc)
|
||
|
|
if !Success(ret) {
|
||
|
|
err := FormatError(C.SQL_HANDLE_DBC, conn.Dbc)
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
conn.connected = false
|
||
|
|
}
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
/* func (stmt *Statement) RowsAffected() (int, *ODBCError)
|
||
|
|
|
||
|
|
修改成func (stmt *Statement) RowsAffected() (int, error)
|
||
|
|
|
||
|
|
*/
|
||
|
|
func (stmt *Statement) RowsAffected() (int, error) {
|
||
|
|
var nor C.SQLLEN
|
||
|
|
ret := C.SQLRowCount(C.SQLHSTMT(stmt.handle), &nor)
|
||
|
|
if !Success(ret) {
|
||
|
|
err := FormatError(C.SQL_HANDLE_STMT, stmt.handle)
|
||
|
|
return -1, err
|
||
|
|
}
|
||
|
|
return int(nor), nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (stmt *Statement) Cancel() *ODBCError {
|
||
|
|
ret := C.SQLCancel(C.SQLHSTMT(stmt.handle))
|
||
|
|
if !Success(ret) {
|
||
|
|
err := FormatError(C.SQL_HANDLE_STMT, stmt.handle)
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (stmt *Statement) NumParams() int {
|
||
|
|
var cParams C.SQLSMALLINT
|
||
|
|
ret := C.SQLNumParams(C.SQLHSTMT(stmt.handle), &cParams)
|
||
|
|
if !Success(ret) {
|
||
|
|
return -1
|
||
|
|
}
|
||
|
|
return int(cParams)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (stmt *Statement) Execute(params ...interface{}) *ODBCError {
|
||
|
|
if params != nil {
|
||
|
|
var cParams C.SQLSMALLINT
|
||
|
|
ret := C.SQLNumParams(C.SQLHSTMT(stmt.handle), &cParams)
|
||
|
|
if !Success(ret) {
|
||
|
|
err := FormatError(C.SQL_HANDLE_STMT, stmt.handle)
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
for i := 0; i < int(cParams); i++ {
|
||
|
|
stmt.BindParam(i+1, params[i])
|
||
|
|
}
|
||
|
|
}
|
||
|
|
ret := C.SQLExecute(C.SQLHSTMT(stmt.handle))
|
||
|
|
if ret == C.SQL_NEED_DATA {
|
||
|
|
// TODO
|
||
|
|
// send_data(stmt)
|
||
|
|
} else if ret == C.SQL_NO_DATA {
|
||
|
|
// Execute NO DATA
|
||
|
|
} else if !Success(ret) {
|
||
|
|
err := FormatError(C.SQL_HANDLE_STMT, stmt.handle)
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
stmt.executed = true
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (stmt *Statement) Execute2(params []driver.Value) *ODBCError {
|
||
|
|
if params != nil {
|
||
|
|
var cParams C.SQLSMALLINT
|
||
|
|
ret := C.SQLNumParams(C.SQLHSTMT(stmt.handle), &cParams)
|
||
|
|
if !Success(ret) {
|
||
|
|
err := FormatError(C.SQL_HANDLE_STMT, stmt.handle)
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
for i := 0; i < int(cParams); i++ {
|
||
|
|
stmt.BindParam(i+1, params[i])
|
||
|
|
}
|
||
|
|
}
|
||
|
|
ret := C.SQLExecute(C.SQLHSTMT(stmt.handle))
|
||
|
|
if ret == C.SQL_NEED_DATA {
|
||
|
|
// TODO
|
||
|
|
// send_data(stmt)
|
||
|
|
} else if ret == C.SQL_NO_DATA {
|
||
|
|
// Execute NO DATA
|
||
|
|
} else if !Success(ret) {
|
||
|
|
err := FormatError(C.SQL_HANDLE_STMT, stmt.handle)
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
stmt.executed = true
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (stmt *Statement) Fetch() (bool, *ODBCError) {
|
||
|
|
ret := C.SQLFetch(C.SQLHSTMT(stmt.handle))
|
||
|
|
if ret == C.SQL_NO_DATA {
|
||
|
|
return false, nil
|
||
|
|
}
|
||
|
|
if !Success(ret) {
|
||
|
|
err := FormatError(C.SQL_HANDLE_STMT, stmt.handle)
|
||
|
|
return false, err
|
||
|
|
}
|
||
|
|
return true, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
type Row struct {
|
||
|
|
Data []interface{}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Get(Columnindex)
|
||
|
|
// TODO Get(ColumnName)
|
||
|
|
func (r *Row) Get(a interface{}) interface{} {
|
||
|
|
value := reflect.ValueOf(a)
|
||
|
|
switch f := value; f.Kind() {
|
||
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||
|
|
return r.Data[f.Int()]
|
||
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||
|
|
return r.Data[f.Uint()]
|
||
|
|
// case *reflect.StringValue:
|
||
|
|
// i := r.Meta[f.Get()]
|
||
|
|
// return r.Data[i]
|
||
|
|
}
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (r *Row) GetInt(a interface{}) (ret int64) {
|
||
|
|
v := r.Get(a)
|
||
|
|
value := reflect.ValueOf(v)
|
||
|
|
switch f := value; f.Kind() {
|
||
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||
|
|
ret = int64(f.Int())
|
||
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||
|
|
ret = int64(f.Uint())
|
||
|
|
}
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
func (r *Row) GetFloat(a interface{}) (ret float64) {
|
||
|
|
v := r.Get(a)
|
||
|
|
value := reflect.ValueOf(v)
|
||
|
|
switch f := value; f.Kind() {
|
||
|
|
case reflect.Float32, reflect.Float64:
|
||
|
|
ret = float64(f.Float())
|
||
|
|
}
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
func (r *Row) GetString(a interface{}) (ret string) {
|
||
|
|
v := r.Get(a)
|
||
|
|
value := reflect.ValueOf(v)
|
||
|
|
switch f := value; f.Kind() {
|
||
|
|
case reflect.String:
|
||
|
|
ret = f.String()
|
||
|
|
}
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
func (r *Row) Length() int {
|
||
|
|
return len(r.Data)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (stmt *Statement) FetchAll() (rows []*Row, err *ODBCError) {
|
||
|
|
for {
|
||
|
|
row, err := stmt.FetchOne()
|
||
|
|
if err != nil || row == nil {
|
||
|
|
break
|
||
|
|
}
|
||
|
|
rows = append(rows, row)
|
||
|
|
}
|
||
|
|
|
||
|
|
return rows, err
|
||
|
|
}
|
||
|
|
|
||
|
|
func (stmt *Statement) FetchOne() (*Row, *ODBCError) {
|
||
|
|
ok, err := stmt.Fetch()
|
||
|
|
if !ok {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
n, _ := stmt.NumFields()
|
||
|
|
row := new(Row)
|
||
|
|
row.Data = make([]interface{}, n)
|
||
|
|
for i := 0; i < n; i++ {
|
||
|
|
v, _, _, _ := stmt.GetField(i)
|
||
|
|
row.Data[i] = v
|
||
|
|
}
|
||
|
|
return row, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (stmt *Statement) FetchOne2(row []driver.Value) (eof bool, err *ODBCError) {
|
||
|
|
ok, err := stmt.Fetch()
|
||
|
|
if !ok && err == nil {
|
||
|
|
return !ok, nil
|
||
|
|
} else if err != nil {
|
||
|
|
return false, err
|
||
|
|
}
|
||
|
|
n, _ := stmt.NumFields()
|
||
|
|
for i := 0; i < n; i++ {
|
||
|
|
v, _, _, _ := stmt.GetField(i)
|
||
|
|
row[i] = v
|
||
|
|
}
|
||
|
|
|
||
|
|
//fmt.Println("row:", row)
|
||
|
|
return false, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (stmt *Statement) GetField(field_index int) (v interface{}, ftype int, flen int, err *ODBCError) {
|
||
|
|
var field_type C.int
|
||
|
|
var field_len C.SQLLEN
|
||
|
|
var ll C.SQLSMALLINT
|
||
|
|
ret := C._SQLColAttribute(C.SQLHSTMT(stmt.handle), C.SQLUSMALLINT(field_index+1), C.SQL_DESC_CONCISE_TYPE, C.SQLPOINTER(unsafe.Pointer(uintptr(0))), C.SQLSMALLINT(0), &ll, unsafe.Pointer(&field_type))
|
||
|
|
if !Success(ret) {
|
||
|
|
// TODO return err
|
||
|
|
}
|
||
|
|
ret = C._SQLColAttribute(C.SQLHSTMT(stmt.handle), C.SQLUSMALLINT(field_index+1), C.SQL_DESC_LENGTH, C.SQLPOINTER(unsafe.Pointer(uintptr(0))), C.SQLSMALLINT(0), &ll, unsafe.Pointer(&field_len))
|
||
|
|
if !Success(ret) {
|
||
|
|
// TODO return err
|
||
|
|
}
|
||
|
|
var fl C.SQLLEN = C.SQLLEN(field_len)
|
||
|
|
switch int(field_type) {
|
||
|
|
case C.SQL_BIT:
|
||
|
|
var value C.BYTE
|
||
|
|
ret = C.SQLGetData(C.SQLHSTMT(stmt.handle), C.SQLUSMALLINT(field_index+1), C.SQL_C_BIT, C.SQLPOINTER(unsafe.Pointer(&value)), 0, &fl)
|
||
|
|
if fl == -1 {
|
||
|
|
v = byte(0)
|
||
|
|
} else {
|
||
|
|
v = byte(value)
|
||
|
|
}
|
||
|
|
case C.SQL_INTEGER, C.SQL_SMALLINT, C.SQL_TINYINT:
|
||
|
|
var value C.long
|
||
|
|
ret = C.SQLGetData(C.SQLHSTMT(stmt.handle), C.SQLUSMALLINT(field_index+1), C.SQL_C_LONG, C.SQLPOINTER(unsafe.Pointer(&value)), 0, &fl)
|
||
|
|
if fl == -1 {
|
||
|
|
v = byte(0)
|
||
|
|
} else {
|
||
|
|
v = int(value)
|
||
|
|
}
|
||
|
|
case C.SQL_BIGINT:
|
||
|
|
var value C.longlong
|
||
|
|
ret = C.SQLGetData(C.SQLHSTMT(stmt.handle), C.SQLUSMALLINT(field_index+1), C.SQL_C_SBIGINT, C.SQLPOINTER(unsafe.Pointer(&value)), 0, &fl)
|
||
|
|
if fl == -1 {
|
||
|
|
v = int64(0)
|
||
|
|
} else {
|
||
|
|
v = int64(value)
|
||
|
|
}
|
||
|
|
case C.SQL_FLOAT, C.SQL_REAL, C.SQL_DOUBLE:
|
||
|
|
var value C.double
|
||
|
|
ret = C.SQLGetData(C.SQLHSTMT(stmt.handle), C.SQLUSMALLINT(field_index+1), C.SQL_C_DOUBLE, C.SQLPOINTER(unsafe.Pointer(&value)), 0, &fl)
|
||
|
|
if fl == -1 {
|
||
|
|
v = float64(0)
|
||
|
|
} else {
|
||
|
|
v = float64(value)
|
||
|
|
}
|
||
|
|
case C.SQL_CHAR, C.SQL_VARCHAR, C.SQL_LONGVARCHAR, C.SQL_WCHAR, C.SQL_WVARCHAR, C.SQL_WLONGVARCHAR:
|
||
|
|
value := make([]uint16, int(field_len)+8)
|
||
|
|
ret = C.SQLGetData(C.SQLHSTMT(stmt.handle), C.SQLUSMALLINT(field_index+1), C.SQL_C_WCHAR, C.SQLPOINTER(unsafe.Pointer(&value[0])), field_len+4, &fl)
|
||
|
|
s := UTF16ToString(value)
|
||
|
|
v = s
|
||
|
|
case C.SQL_TYPE_TIMESTAMP, C.SQL_TYPE_DATE, C.SQL_TYPE_TIME, C.SQL_DATETIME:
|
||
|
|
var value C.TIMESTAMP_STRUCT
|
||
|
|
ret = C.SQLGetData(C.SQLHSTMT(stmt.handle), C.SQLUSMALLINT(field_index+1), C.SQL_C_TYPE_TIMESTAMP, C.SQLPOINTER(unsafe.Pointer(&value)), C.SQLLEN(unsafe.Sizeof(value)), &fl)
|
||
|
|
if fl == -1 {
|
||
|
|
v = nil
|
||
|
|
} else {
|
||
|
|
v = time.Date(int(value.year), time.Month(value.month), int(value.day), int(value.hour), int(value.minute), int(value.second), int(value.fraction), time.UTC)
|
||
|
|
}
|
||
|
|
case C.SQL_BINARY, C.SQL_VARBINARY, C.SQL_LONGVARBINARY:
|
||
|
|
var vv int
|
||
|
|
ret = C.SQLGetData(C.SQLHSTMT(stmt.handle), C.SQLUSMALLINT(field_index+1), C.SQL_C_BINARY, C.SQLPOINTER(unsafe.Pointer(&vv)), 0, &fl)
|
||
|
|
if fl == -1 {
|
||
|
|
v = nil
|
||
|
|
} else {
|
||
|
|
value := make([]byte, fl)
|
||
|
|
ret = C.SQLGetData(C.SQLHSTMT(stmt.handle), C.SQLUSMALLINT(field_index+1), C.SQL_C_BINARY, C.SQLPOINTER(unsafe.Pointer(&value[0])), C.SQLLEN(fl), &fl)
|
||
|
|
v = value
|
||
|
|
}
|
||
|
|
default:
|
||
|
|
value := make([]byte, field_len)
|
||
|
|
ret = C.SQLGetData(C.SQLHSTMT(stmt.handle), C.SQLUSMALLINT(field_index+1), C.SQL_C_BINARY, C.SQLPOINTER(unsafe.Pointer(&value[0])), field_len, &fl)
|
||
|
|
v = value
|
||
|
|
}
|
||
|
|
if !Success(ret) {
|
||
|
|
err = FormatError(C.SQL_HANDLE_STMT, stmt.handle)
|
||
|
|
}
|
||
|
|
return v, int(field_type), int(fl), err
|
||
|
|
}
|
||
|
|
|
||
|
|
func (stmt *Statement) NumFields() (int, *ODBCError) {
|
||
|
|
var NOC C.SQLSMALLINT
|
||
|
|
ret := C.SQLNumResultCols(C.SQLHSTMT(stmt.handle), &NOC)
|
||
|
|
if !Success(ret) {
|
||
|
|
err := FormatError(C.SQL_HANDLE_STMT, stmt.handle)
|
||
|
|
return -1, err
|
||
|
|
}
|
||
|
|
return int(NOC), nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (stmt *Statement) GetParamType(index int) (int, int, int, int, *ODBCError) {
|
||
|
|
var data_type, dec_ptr, null_ptr C.SQLSMALLINT
|
||
|
|
var size_ptr C.SQLULEN
|
||
|
|
ret := C.SQLDescribeParam(C.SQLHSTMT(stmt.handle), C.SQLUSMALLINT(index), &data_type, &size_ptr, &dec_ptr, &null_ptr)
|
||
|
|
if !Success(ret) {
|
||
|
|
err := FormatError(C.SQL_HANDLE_STMT, stmt.handle)
|
||
|
|
return -1, -1, -1, -1, err
|
||
|
|
}
|
||
|
|
return int(data_type), int(size_ptr), int(dec_ptr), int(null_ptr), nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (stmt *Statement) BindParam(index int, param interface{}) *ODBCError {
|
||
|
|
var ValueType C.SQLSMALLINT
|
||
|
|
var ParameterType C.SQLSMALLINT
|
||
|
|
var ColumnSize C.SQLULEN
|
||
|
|
var DecimalDigits C.SQLSMALLINT
|
||
|
|
var ParameterValuePtr C.SQLPOINTER
|
||
|
|
var BufferLength C.SQLLEN
|
||
|
|
var StrLen_or_IndPt C.SQLLEN
|
||
|
|
v := reflect.ValueOf(param)
|
||
|
|
if param == nil {
|
||
|
|
ft, _, _, _, err := stmt.GetParamType(index)
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
ParameterType = C.SQLSMALLINT(ft)
|
||
|
|
if ParameterType == C.SQL_UNKNOWN_TYPE {
|
||
|
|
ParameterType = C.SQL_VARCHAR
|
||
|
|
}
|
||
|
|
ValueType = C.SQL_C_DEFAULT
|
||
|
|
StrLen_or_IndPt = C.SQL_NULL_DATA
|
||
|
|
ColumnSize = 1
|
||
|
|
} else {
|
||
|
|
switch v.Kind() {
|
||
|
|
case reflect.Bool:
|
||
|
|
ParameterType = C.SQL_BIT
|
||
|
|
ValueType = C.SQL_C_BIT
|
||
|
|
var b [1]byte
|
||
|
|
if v.Bool() {
|
||
|
|
b[0] = 1
|
||
|
|
} else {
|
||
|
|
b[0] = 0
|
||
|
|
}
|
||
|
|
ParameterValuePtr = C.SQLPOINTER(unsafe.Pointer(&b[0]))
|
||
|
|
BufferLength = 1
|
||
|
|
StrLen_or_IndPt = 0
|
||
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||
|
|
switch v.Type().Kind() {
|
||
|
|
case reflect.Int:
|
||
|
|
case reflect.Int8, reflect.Int16, reflect.Int32:
|
||
|
|
ParameterType = C.SQL_INTEGER
|
||
|
|
ValueType = C.SQL_C_LONG
|
||
|
|
var l C.long = C.long(v.Int())
|
||
|
|
ParameterValuePtr = C.SQLPOINTER(unsafe.Pointer(&l))
|
||
|
|
BufferLength = 4
|
||
|
|
StrLen_or_IndPt = 0
|
||
|
|
case reflect.Int64:
|
||
|
|
ParameterType = C.SQL_BIGINT
|
||
|
|
ValueType = C.SQL_C_SBIGINT
|
||
|
|
var ll C.longlong = C.longlong(v.Int())
|
||
|
|
ParameterValuePtr = C.SQLPOINTER(unsafe.Pointer(&ll))
|
||
|
|
BufferLength = 8
|
||
|
|
StrLen_or_IndPt = 0
|
||
|
|
}
|
||
|
|
case reflect.Float32, reflect.Float64:
|
||
|
|
ParameterType = C.SQL_DOUBLE
|
||
|
|
ValueType = C.SQL_C_DOUBLE
|
||
|
|
var d C.double = C.double(v.Float())
|
||
|
|
ParameterValuePtr = C.SQLPOINTER(unsafe.Pointer(&d))
|
||
|
|
BufferLength = 8
|
||
|
|
StrLen_or_IndPt = 0
|
||
|
|
case reflect.Complex64, reflect.Complex128:
|
||
|
|
case reflect.String:
|
||
|
|
var slen C.SQLUINTEGER = C.SQLUINTEGER(len(v.String()))
|
||
|
|
ParameterType = C.SQL_VARCHAR
|
||
|
|
ValueType = C.SQL_C_CHAR
|
||
|
|
s := []byte(v.String())
|
||
|
|
ParameterValuePtr = C.SQLPOINTER(unsafe.Pointer(&s[0]))
|
||
|
|
ColumnSize = C.SQLULEN(slen)
|
||
|
|
BufferLength = C.SQLLEN(slen + 1)
|
||
|
|
StrLen_or_IndPt = C.SQLLEN(slen)
|
||
|
|
default:
|
||
|
|
fmt.Println("Not support type", v)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
ret := C.SQLBindParameter(C.SQLHSTMT(stmt.handle), C.SQLUSMALLINT(index), C.SQL_PARAM_INPUT, ValueType, ParameterType, ColumnSize, DecimalDigits, ParameterValuePtr, BufferLength, &StrLen_or_IndPt)
|
||
|
|
if !Success(ret) {
|
||
|
|
err := FormatError(C.SQL_HANDLE_STMT, stmt.handle)
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (stmt *Statement) NextResult() bool {
|
||
|
|
ret := C.SQLMoreResults(C.SQLHSTMT(stmt.handle))
|
||
|
|
if ret == C.SQL_NO_DATA {
|
||
|
|
return false
|
||
|
|
}
|
||
|
|
return true
|
||
|
|
}
|
||
|
|
|
||
|
|
func (stmt *Statement) NumRows() (int, *ODBCError) {
|
||
|
|
var NOR C.SQLLEN
|
||
|
|
ret := C.SQLRowCount(C.SQLHSTMT(stmt.handle), &NOR)
|
||
|
|
if !Success(ret) {
|
||
|
|
err := FormatError(C.SQL_HANDLE_STMT, stmt.handle)
|
||
|
|
return -1, err
|
||
|
|
}
|
||
|
|
return int(NOR), nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (stmt *Statement) HasRows() bool {
|
||
|
|
n, _ := stmt.NumRows()
|
||
|
|
return n > 0
|
||
|
|
}
|
||
|
|
|
||
|
|
type Field struct {
|
||
|
|
Name string
|
||
|
|
Type int
|
||
|
|
Size int
|
||
|
|
DecimalDigits int
|
||
|
|
Nullable int
|
||
|
|
}
|
||
|
|
|
||
|
|
func (stmt *Statement) FieldMetadata(col int) (*Field, *ODBCError) {
|
||
|
|
var BufferLength C.SQLSMALLINT = INFO_BUFFER_LEN
|
||
|
|
var NameLength C.SQLSMALLINT
|
||
|
|
var DataType C.SQLSMALLINT
|
||
|
|
var ColumnSize C.SQLULEN
|
||
|
|
var DecimalDigits C.SQLSMALLINT
|
||
|
|
var Nullable C.SQLSMALLINT
|
||
|
|
ColumnName := make([]byte, INFO_BUFFER_LEN)
|
||
|
|
ret := C.SQLDescribeCol(C.SQLHSTMT(stmt.handle),
|
||
|
|
C.SQLUSMALLINT(col),
|
||
|
|
(*C.SQLCHAR)(unsafe.Pointer(&ColumnName[0])),
|
||
|
|
BufferLength,
|
||
|
|
&NameLength,
|
||
|
|
&DataType,
|
||
|
|
&ColumnSize,
|
||
|
|
&DecimalDigits,
|
||
|
|
&Nullable)
|
||
|
|
if !Success(ret) {
|
||
|
|
err := FormatError(C.SQL_HANDLE_STMT, stmt.handle)
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
field := &Field{string(ColumnName[0:NameLength]), int(DataType), int(ColumnSize), int(DecimalDigits), int(Nullable)}
|
||
|
|
return field, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (stmt *Statement) free() {
|
||
|
|
if stmt.handle != nil {
|
||
|
|
C.SQLFreeHandle(C.SQL_HANDLE_STMT, stmt.handle)
|
||
|
|
stmt.handle = nil
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func (stmt *Statement) Close() {
|
||
|
|
stmt.free()
|
||
|
|
}
|
||
|
|
|
||
|
|
func Success(ret C.SQLRETURN) bool {
|
||
|
|
return int(ret) == C.SQL_SUCCESS || int(ret) == C.SQL_SUCCESS_WITH_INFO
|
||
|
|
}
|
||
|
|
|
||
|
|
func FormatError(ht C.SQLSMALLINT, h C.SQLHANDLE) (err *ODBCError) {
|
||
|
|
sqlState := make([]uint16, 6)
|
||
|
|
var nativeError C.SQLINTEGER
|
||
|
|
messageText := make([]uint16, C.SQL_MAX_MESSAGE_LENGTH)
|
||
|
|
var textLength C.SQLSMALLINT
|
||
|
|
err = &ODBCError{}
|
||
|
|
i := 0
|
||
|
|
for {
|
||
|
|
i++
|
||
|
|
ret := C.SQLGetDiagRecW(C.SQLSMALLINT(ht),
|
||
|
|
h,
|
||
|
|
C.SQLSMALLINT(i),
|
||
|
|
(*C.SQLWCHAR)(unsafe.Pointer(&sqlState[0])),
|
||
|
|
&nativeError,
|
||
|
|
(*C.SQLWCHAR)(unsafe.Pointer(&messageText[0])),
|
||
|
|
C.SQL_MAX_MESSAGE_LENGTH,
|
||
|
|
&textLength)
|
||
|
|
if ret == C.SQL_INVALID_HANDLE || ret == C.SQL_NO_DATA {
|
||
|
|
break
|
||
|
|
}
|
||
|
|
if i == 1 { // first error message save the SQLSTATE.
|
||
|
|
err.SQLState = UTF16ToString(sqlState)
|
||
|
|
err.NativeError = int(nativeError)
|
||
|
|
}
|
||
|
|
err.ErrorMessage += UTF16ToString(messageText)
|
||
|
|
}
|
||
|
|
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
func init() {
|
||
|
|
if err := initEnv(); err != nil {
|
||
|
|
panic("odbc init env error!" + err.String())
|
||
|
|
}
|
||
|
|
}
|