func (c *Conn) PrepareODBCStmt(query string) (*ODBCStmt, error) { var out api.SQLHANDLE ret := api.SQLAllocHandle(api.SQL_HANDLE_STMT, api.SQLHANDLE(c.h), &out) if IsError(ret) { return nil, c.newError("SQLAllocHandle", c.h) } h := api.SQLHSTMT(out) drv.Stats.updateHandleCount(api.SQL_HANDLE_STMT, 1) b := api.StringToUTF16(query) ret = api.SQLPrepare(h, (*api.SQLWCHAR)(unsafe.Pointer(&b[0])), api.SQL_NTS) if IsError(ret) { defer releaseHandle(h) return nil, c.newError("SQLPrepare", h) } ps, err := ExtractParameters(h) if err != nil { defer releaseHandle(h) return nil, err } return &ODBCStmt{ h: h, Parameters: ps, usedByStmt: true, }, nil }
func (d *Driver) Open(dsn string) (driver.Conn, error) { var out api.SQLHANDLE ret := api.SQLAllocHandle(api.SQL_HANDLE_DBC, api.SQLHANDLE(d.h), &out) if IsError(ret) { return nil, NewError("SQLAllocHandle", d.h) } h := api.SQLHDBC(out) drv.Stats.updateHandleCount(api.SQL_HANDLE_DBC, 1) b := api.StringToUTF16(dsn) ret = api.SQLDriverConnect(h, 0, (*api.SQLWCHAR)(unsafe.Pointer(&b[0])), api.SQL_NTS, nil, 0, nil, api.SQL_DRIVER_NOPROMPT) if IsError(ret) { defer releaseHandle(h) return nil, NewError("SQLDriverConnect", h) } return &Conn{h: h}, nil }
func (p *Parameter) BindValue(h api.SQLHSTMT, idx int, v driver.Value) error { // TODO(brainman): Reuse memory for previously bound values. If memory // is reused, we, probably, do not need to call SQLBindParameter either. var ctype, sqltype, decimal api.SQLSMALLINT var size api.SQLULEN var buflen api.SQLLEN var plen *api.SQLLEN var buf unsafe.Pointer switch d := v.(type) { case nil: ctype = api.SQL_C_WCHAR p.Data = nil buf = nil size = 1 buflen = 0 plen = p.StoreStrLen_or_IndPtr(api.SQL_NULL_DATA) sqltype = api.SQL_WCHAR case string: ctype = api.SQL_C_WCHAR b := api.StringToUTF16(d) p.Data = b buf = unsafe.Pointer(&b[0]) l := len(b) l -= 1 // remove terminating 0 size = api.SQLULEN(l) if size < 1 { // size cannot be less then 1 even for empty fields size = 1 } l *= 2 // every char takes 2 bytes buflen = api.SQLLEN(l) plen = p.StoreStrLen_or_IndPtr(buflen) switch { case size >= 4000: sqltype = api.SQL_WLONGVARCHAR case p.isDescribed: sqltype = p.SQLType case size <= 1: sqltype = api.SQL_WVARCHAR default: sqltype = api.SQL_WCHAR } case int64: ctype = api.SQL_C_SBIGINT p.Data = &d buf = unsafe.Pointer(&d) sqltype = api.SQL_BIGINT size = 8 case bool: var b byte if d { b = 1 } ctype = api.SQL_C_BIT p.Data = &b buf = unsafe.Pointer(&b) sqltype = api.SQL_BIT size = 1 case float64: ctype = api.SQL_C_DOUBLE p.Data = &d buf = unsafe.Pointer(&d) sqltype = api.SQL_DOUBLE size = 8 case time.Time: ctype = api.SQL_C_TYPE_TIMESTAMP y, m, day := d.Date() b := api.SQL_TIMESTAMP_STRUCT{ Year: api.SQLSMALLINT(y), Month: api.SQLUSMALLINT(m), Day: api.SQLUSMALLINT(day), Hour: api.SQLUSMALLINT(d.Hour()), Minute: api.SQLUSMALLINT(d.Minute()), Second: api.SQLUSMALLINT(d.Second()), Fraction: 0, } p.Data = &b buf = unsafe.Pointer(&b) sqltype = api.SQL_TYPE_TIMESTAMP if p.isDescribed && p.SQLType == api.SQL_TYPE_TIMESTAMP { decimal = p.Decimal } if decimal <= 0 { // represented as yyyy-mm-dd hh:mm:ss.fff format in ms sql server decimal = 3 b.Fraction = api.SQLUINTEGER(d.Nanosecond() / int(time.Millisecond)) } size = 20 + api.SQLULEN(decimal) case []byte: if nil == d { ctype = api.SQL_C_WCHAR p.Data = nil buf = nil size = 1 buflen = 0 plen = p.StoreStrLen_or_IndPtr(api.SQL_NULL_DATA) sqltype = api.SQL_WCHAR } else { ctype = api.SQL_C_BINARY b := make([]byte, len(d)) copy(b, d) p.Data = b buf = unsafe.Pointer(&b[0]) buflen = api.SQLLEN(len(b)) plen = p.StoreStrLen_or_IndPtr(buflen) size = api.SQLULEN(len(b)) switch { case p.isDescribed: sqltype = p.SQLType case size <= 0: sqltype = api.SQL_LONGVARBINARY case size >= 8000: sqltype = api.SQL_LONGVARBINARY default: sqltype = api.SQL_BINARY } } default: panic(fmt.Errorf("unsupported type %T", v)) } ret := api.SQLBindParameter(h, api.SQLUSMALLINT(idx+1), api.SQL_PARAM_INPUT, ctype, sqltype, size, decimal, api.SQLPOINTER(buf), buflen, plen) if IsError(ret) { return NewError("SQLBindParameter", h) } return nil }