func ExtractParameters(h api.SQLHSTMT) ([]Parameter, error) { // count parameters var n, nullable api.SQLSMALLINT ret := api.SQLNumParams(h, &n) if IsError(ret) { err := NewError("SQLNumParams", h) if strings.Contains(err.Error(), "IM001") { return nil, nil } return nil, err } if n <= 0 { // no parameters return nil, nil } ps := make([]Parameter, n) // fetch param descriptions for i := range ps { p := &ps[i] ret = api.SQLDescribeParam(h, api.SQLUSMALLINT(i+1), &p.SQLType, &p.Size, &p.Decimal, &nullable) if IsError(ret) { // SQLDescribeParam is not implemented by freedts, // it even fails for some statements on windows. // Will try request without these descriptions continue } p.isDescribed = true } return ps, nil }
func describeColumn(h api.SQLHSTMT, idx int, namebuf []uint16) (namelen int, sqltype api.SQLSMALLINT, size api.SQLULEN, ret api.SQLRETURN) { var l, decimal, nullable api.SQLSMALLINT ret = api.SQLDescribeCol(h, api.SQLUSMALLINT(idx+1), (*api.SQLWCHAR)(unsafe.Pointer(&namebuf[0])), api.SQLSMALLINT(len(namebuf)), &l, &sqltype, &size, &decimal, &nullable) return int(l), sqltype, size, ret }
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: api.SQLUINTEGER(d.Nanosecond()), } 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 } size = 20 + api.SQLULEN(decimal) case []byte: 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 }
func (l *BufferLen) Bind(h api.SQLHSTMT, idx int, ctype api.SQLSMALLINT, buf []byte) api.SQLRETURN { return api.SQLBindCol(h, api.SQLUSMALLINT(idx+1), ctype, api.SQLPOINTER(unsafe.Pointer(&buf[0])), api.SQLLEN(len(buf)), (*api.SQLLEN)(l)) }