func (stmt *statement) bindDate(index int, value time.Time, direction ParameterDirection) error { var bindVal odbc.SQL_DATE_STRUCT bindVal.Year = odbc.SQLSMALLINT(value.Year()) bindVal.Month = odbc.SQLUSMALLINT(value.Month()) bindVal.Day = odbc.SQLUSMALLINT(value.Day()) stmt.bindValues[index] = &bindVal ret := odbc.SQLBindParameter(stmt.handle, odbc.SQLUSMALLINT(index), direction.SQLBindParameterType(), odbc.SQL_C_DATE, odbc.SQL_DATE, 10, 0, odbc.SQLPOINTER(unsafe.Pointer(stmt.bindValues[index].(*odbc.SQL_DATE_STRUCT))), 6, nil) if isError(ret) { return errorStatement(stmt.handle, fmt.Sprintf("Bind index: %v, Value: %v", index, bindVal)) } return nil }
func (stmt *statement) bindByteArray(index int, value []byte, direction ParameterDirection) error { // Store both value and lenght, because we need a pointer to the lenght in // the last parameter of SQLBindParamter. Otherwise the data is assumed to // be a null terminated string. bindVal := &struct { value []byte length int }{ value, len(value), } sqlType := odbc.SQL_VARBINARY if bindVal.length > 4000 { sqlType = odbc.SQL_LONGVARBINARY } // Protect against index out of range on &bindVal.value[0] when value is zero-length. // We can't pass NULL to SQLBindParameter so this is needed, it will still // write a zero length value to the database since the length parameter is // zero. if bindVal.length == 0 { bindVal.value = []byte{'\x00'} } ret := odbc.SQLBindParameter(stmt.handle, odbc.SQLUSMALLINT(index), direction.SQLBindParameterType(), odbc.SQL_C_BINARY, sqlType, odbc.SQLULEN(bindVal.length), 0, odbc.SQLPOINTER(unsafe.Pointer(&bindVal.value[0])), 0, (*odbc.SQLLEN)(unsafe.Pointer(&bindVal.length))) if isError(ret) { return errorStatement(stmt.handle, fmt.Sprintf("Bind index: %v, Value: %v", index, value)) } return nil }
func (stmt *statement) bindBool(index int, value bool, direction ParameterDirection) error { stmt.bindValues[index] = &value ret := odbc.SQLBindParameter(stmt.handle, odbc.SQLUSMALLINT(index), direction.SQLBindParameterType(), odbc.SQL_C_BIT, odbc.SQL_BIT, 0, 0, odbc.SQLPOINTER(unsafe.Pointer(stmt.bindValues[index].(*bool))), 0, nil) if isError(ret) { return errorStatement(stmt.handle, fmt.Sprintf("Bind index: %v, Value: %v", index, value)) } return nil }
func (stmt *statement) bindNullParam(index int, paramType odbc.SQLDataType, direction ParameterDirection) error { nullDataInd := odbc.SQL_NULL_DATA stmt.bindValues[index] = &nullDataInd ret := odbc.SQLBindParameter(stmt.handle, odbc.SQLUSMALLINT(index), direction.SQLBindParameterType(), odbc.SQL_C_DEFAULT, paramType, 1, 0, 0, 0, &nullDataInd) if isError(ret) { return errorStatement(stmt.handle, fmt.Sprintf("Bind index: %v, Value: nil", index)) } return nil }
func (stmt *statement) bindNumeric(index int, value float64, precision int, scale int, direction ParameterDirection) error { stmt.bindValues[index] = &value ret := odbc.SQLBindParameter(stmt.handle, odbc.SQLUSMALLINT(index), direction.SQLBindParameterType(), odbc.SQL_C_DOUBLE, odbc.SQL_DOUBLE, 0, 0, odbc.SQLPOINTER(unsafe.Pointer(stmt.bindValues[index].(*float64))), 0, nil) /* Must convert to SQL_NUMERIC_STRUCT for decimal to work - http://support.microsoft.com/kb/181254 ret := odbc.SQLBindParameter(stmt.handle, uint16(index), direction.SQLBindParameterType(), odbc.SQL_C_NUMERIC, odbc.SQL_DECIMAL, uint64(precision), int16(scale), uintptr(unsafe.Pointer(&bindVal)), 0, nil) odbc.SQLSetDescField(stmt.stmtDescHandle, odbc.SQLSMALLINT(index), odbc.SQL_DESC_TYPE, odbc.SQL_NUMERIC, 0) odbc.SQLSetDescField(stmt.stmtDescHandle, odbc.SQLSMALLINT(index), odbc.SQL_DESC_PRECISION, int32(precision), 0) odbc.SQLSetDescField(stmt.stmtDescHandle, odbc.SQLSMALLINT(index), odbc.SQL_DESC_SCALE, int32(scale), 0) */ if isError(ret) { return errorStatement(stmt.handle, fmt.Sprintf("Bind index: %v, Value: %v", index, value)) } return nil }
func (stmt *statement) bindString(index int, value string, length int, direction ParameterDirection) error { if length == 0 { length = len(value) } stmt.bindValues[index] = syscall.StringToUTF16(value) var sqlType odbc.SQLDataType if length < 4000 { sqlType = odbc.SQL_VARCHAR } else { sqlType = odbc.SQL_LONGVARCHAR } ret := odbc.SQLBindParameter(stmt.handle, odbc.SQLUSMALLINT(index), direction.SQLBindParameterType(), odbc.SQL_C_WCHAR, sqlType, odbc.SQLULEN(length), 0, odbc.SQLPOINTER(unsafe.Pointer(&stmt.bindValues[index].([]uint16)[0])), 0, nil) if isError(ret) { return errorStatement(stmt.handle, fmt.Sprintf("Bind index: %v, Value: %v", index, value)) } return nil }
// Return a single column of data func (rows *rows) getField(index int) (v interface{}, ret odbc.SQLReturn) { columnDef := rows.resultColumnDefs[index-1] var fieldInd odbc.SQLLEN switch columnDef.DataType { case odbc.SQL_BIT: var value bool valuePtr := uintptr(unsafe.Pointer(&value)) ret = odbc.SQLGetData(rows.handle, odbc.SQLUSMALLINT(index), odbc.SQL_C_BIT, valuePtr, 0, &fieldInd) return formatGetFieldReturn(value, fieldInd, ret) case odbc.SQL_INTEGER, odbc.SQL_SMALLINT, odbc.SQL_TINYINT: var value int valuePtr := uintptr(unsafe.Pointer(&value)) ret = odbc.SQLGetData(rows.handle, odbc.SQLUSMALLINT(index), odbc.SQL_C_LONG, valuePtr, 0, &fieldInd) return formatGetFieldReturn(value, fieldInd, ret) case odbc.SQL_BIGINT: var value int64 valuePtr := uintptr(unsafe.Pointer(&value)) ret = odbc.SQLGetData(rows.handle, odbc.SQLUSMALLINT(index), odbc.SQL_C_LONG, valuePtr, 0, &fieldInd) return formatGetFieldReturn(value, fieldInd, ret) case odbc.SQL_FLOAT: var value float64 valuePtr := uintptr(unsafe.Pointer(&value)) ret = odbc.SQLGetData(rows.handle, odbc.SQLUSMALLINT(index), odbc.SQL_C_FLOAT, valuePtr, 0, &fieldInd) return formatGetFieldReturn(value, fieldInd, ret) case odbc.SQL_DOUBLE, odbc.SQL_REAL: var value float64 valuePtr := uintptr(unsafe.Pointer(&value)) ret = odbc.SQLGetData(rows.handle, odbc.SQLUSMALLINT(index), odbc.SQL_C_DOUBLE, valuePtr, 0, &fieldInd) return formatGetFieldReturn(value, fieldInd, ret) case odbc.SQL_NUMERIC, odbc.SQL_DECIMAL: var value odbc.SQL_NUMERIC_STRUCT valuePtr := uintptr(unsafe.Pointer(&value)) ret = odbc.SQLGetData(rows.handle, odbc.SQLUSMALLINT(index), odbc.SQL_ARD_TYPE, valuePtr, 0, &fieldInd) return formatGetFieldReturn(numericToFloat(value), fieldInd, ret) case odbc.SQL_CHAR, odbc.SQL_VARCHAR, odbc.SQL_LONGVARCHAR, odbc.SQL_WCHAR, odbc.SQL_WVARCHAR, odbc.SQL_SS_XML: //Must read string in chunks stringParts := make([]string, 0) for { chunkSize := 4096 valueChunk := make([]uint16, chunkSize*2) valueChunkPtr := uintptr(unsafe.Pointer(&valueChunk[0])) ret = odbc.SQLGetData(rows.handle, odbc.SQLUSMALLINT(index), odbc.SQL_C_WCHAR, valueChunkPtr, odbc.SQLLEN(chunkSize*2*2), &fieldInd) if isError(ret) || odbc.SQLLEN(ret) == odbc.SQL_NULL_DATA { return formatGetFieldReturn(nil, fieldInd, ret) } else if ret == odbc.SQL_NO_DATA { //All data has been retrieved break } else if ret == odbc.SQL_SUCCESS { stringParts = append(stringParts, syscall.UTF16ToString(valueChunk)) break } stringParts = append(stringParts, syscall.UTF16ToString(valueChunk)) } return formatGetFieldReturn(strings.Join(stringParts, ""), odbc.SQLLEN(0), odbc.SQL_SUCCESS) case odbc.SQL_VARBINARY: var binaryData []byte chunkSize := 4096 valueChunk := make([]byte, chunkSize) valueChunkPtr := uintptr(unsafe.Pointer(&valueChunk[0])) for { ret = odbc.SQLGetData(rows.handle, odbc.SQLUSMALLINT(index), odbc.SQL_C_BINARY, valueChunkPtr, odbc.SQLLEN(chunkSize), &fieldInd) if isError(ret) || odbc.SQLLEN(ret) == odbc.SQL_NULL_DATA { return formatGetFieldReturn(nil, fieldInd, ret) } else if ret == odbc.SQL_NO_DATA { //All data has been retrieved break } else if ret == odbc.SQL_SUCCESS { partSize := int(fieldInd) % chunkSize if partSize == 0 { partSize = chunkSize } binaryData = append(binaryData, valueChunk[0:partSize]...) break } binaryData = append(binaryData, valueChunk...) } return formatGetFieldReturn(binaryData, odbc.SQLLEN(0), odbc.SQL_SUCCESS) case odbc.SQL_TYPE_DATE: var value odbc.SQL_DATE_STRUCT valuePtr := uintptr(unsafe.Pointer(&value)) ret = odbc.SQLGetData(rows.handle, odbc.SQLUSMALLINT(index), odbc.SQL_C_DATE, valuePtr, 0, &fieldInd) time := time.Date(int(value.Year), time.Month(value.Month), int(value.Day), 0, 0, 0, 0, time.UTC) return formatGetFieldReturn(time, fieldInd, ret) case odbc.SQL_TYPE_TIMESTAMP: var value odbc.SQL_TIMESTAMP_STRUCT valuePtr := uintptr(unsafe.Pointer(&value)) ret = odbc.SQLGetData(rows.handle, odbc.SQLUSMALLINT(index), odbc.SQL_C_TIMESTAMP, valuePtr, 0, &fieldInd) time := time.Date(int(value.Year), time.Month(value.Month), int(value.Day), int(value.Hour), int(value.Minute), int(value.Second), int(value.Faction), time.UTC) return formatGetFieldReturn(time, fieldInd, ret) default: panic(fmt.Sprintf("ODBC type not supported: {%v}. Column name: %v", columnDef.DataType, columnDef.Name)) } return nil, odbc.SQL_SUCCESS }
// Build metadata for each result column func buildResultColumnDefinitions(stmtHandle odbc.SQLHandle, sqlStmt string) ([]resultColumnDef, odbc.SQLReturn) { //Get number of result columns var numColumns odbc.SQLSMALLINT ret := odbc.SQLNumResultCols(stmtHandle, &numColumns) if isError(ret) { errorStatement(stmtHandle, sqlStmt) } resultColumnDefs := make([]resultColumnDef, 0, numColumns) for colNum, lNumColumns := odbc.SQLSMALLINT(1), numColumns; colNum <= lNumColumns; colNum++ { //Get odbc.SQL type var sqlType odbc.SQLLEN ret := odbc.SQLColAttribute(stmtHandle, odbc.SQLUSMALLINT(colNum), odbc.SQL_COLUMN_TYPE, 0, 0, nil, &sqlType) if isError(ret) { errorStatement(stmtHandle, sqlStmt) } /* Disabled because it is no longer needed //Get length var length odbc.SQLLEN ret = odbc.SQLColAttribute(stmtHandle, odbc.SQLUSMALLINT(colNum), odbc.SQL_COLUMN_LENGTH, 0, 0, nil, &length) if isError(ret) { errorStatement(stmtHandle, sqlStmt) } //If the type is a CHAR or VARCHAR, add 4 to the length if odbc.SQLDataType(sqlType) == odbc.SQL_CHAR || odbc.SQLDataType(sqlType) == odbc.SQL_VARCHAR || odbc.SQLDataType(sqlType) == odbc.SQL_WCHAR || odbc.SQLDataType(sqlType) == odbc.SQL_WVARCHAR { length = length + 4 } */ //Get name const namelength = 1000 nameArr := make([]uint16, namelength) ret = odbc.SQLColAttribute(stmtHandle, odbc.SQLUSMALLINT(colNum), odbc.SQL_DESC_LABEL, uintptr(unsafe.Pointer(&nameArr[0])), namelength, nil, nil) if isError(ret) { errorStatement(stmtHandle, sqlStmt) } name := syscall.UTF16ToString(nameArr) //For numeric and decimal types, get the precision var precision odbc.SQLLEN if odbc.SQLDataType(sqlType) == odbc.SQL_NUMERIC || odbc.SQLDataType(sqlType) == odbc.SQL_DECIMAL { ret = odbc.SQLColAttribute(stmtHandle, odbc.SQLUSMALLINT(colNum), odbc.SQL_COLUMN_PRECISION, 0, 0, nil, &precision) if isError(ret) { errorStatement(stmtHandle, sqlStmt) } } //For numeric and decimal types, get the scale var scale odbc.SQLLEN if odbc.SQLDataType(sqlType) == odbc.SQL_NUMERIC || odbc.SQLDataType(sqlType) == odbc.SQL_DECIMAL { ret = odbc.SQLColAttribute(stmtHandle, odbc.SQLUSMALLINT(colNum), odbc.SQL_COLUMN_SCALE, 0, 0, nil, &scale) if isError(ret) { errorStatement(stmtHandle, sqlStmt) } } col := resultColumnDef{RecNum: colNum, DataType: odbc.SQLDataType(sqlType), Name: name, Precision: precision, Scale: scale} resultColumnDefs = append(resultColumnDefs, col) } return resultColumnDefs, odbc.SQL_SUCCESS }