3 Star 0 Fork 0

GBase8s/go-gci

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
statement.go 32.63 KB
一键复制 编辑 原始数据 按行查看 历史
617206652 提交于 2024-07-05 11:57 . add SQLT_FLT case
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040
package gbase8s
// #include "gbase8s.go.h"
import "C"
import (
"bytes"
"context"
"database/sql"
"database/sql/driver"
"encoding/binary"
"fmt"
"strings"
"time"
"unsafe"
)
// Close closes the statement
func (stmt *Stmt) Close() error {
if stmt.closed {
return nil
}
stmt.closed = true
var result C.sword
if stmt.cacheKey == "" {
result = C.GCIStmtRelease(
stmt.stmt, // statement handle
stmt.conn.errHandle, // error handle
nil, // key to be assgciated with the statement in the cache
C.ub4(0), // length of the key
stmt.releaseMode, // mode
)
} else {
cacheKeyP := cString(stmt.cacheKey)
defer C.free(unsafe.Pointer(cacheKeyP))
result = C.GCIStmtRelease(
stmt.stmt, // statement handle
stmt.conn.errHandle, // error handle
cacheKeyP, // key to be assgciated with the statement in the cache
C.ub4(len(stmt.cacheKey)), // length of the key
stmt.releaseMode, // mode
)
}
stmt.stmt = nil
return stmt.conn.getError(result)
}
// NumInput returns the number of input
func (stmt *Stmt) NumInput() int {
return -1
}
// CheckNamedValue checks a named value
func (stmt *Stmt) CheckNamedValue(namedValue *driver.NamedValue) error {
switch namedValue.Value.(type) {
case sql.Out:
return nil
}
return driver.ErrSkip
}
// bindValues binds the values to the stmt
func (stmt *Stmt) bindValues(values []driver.Value, namedValues []driver.NamedValue) ([]bindStruct, error) {
if len(values) == 0 && len(namedValues) == 0 {
return nil, nil
}
var err error
var binds []bindStruct
var useValues bool
count := len(namedValues)
if count == 0 {
useValues = true
count = len(values)
}
for i := 0; i < count; i++ {
if stmt.ctx.Err() != nil {
freeBinds(binds)
return nil, stmt.ctx.Err()
}
var valueInterface interface{}
var sbind bindStruct
sbind.length = (*C.ub2)(C.malloc(C.sizeof_ub2))
*sbind.length = 0
sbind.indicator = (*C.sb2)(C.malloc(C.sizeof_sb2))
*sbind.indicator = 0
if useValues {
valueInterface = values[i]
} else {
valueInterface = namedValues[i].Value
}
var isOut bool
var isNill bool
sbind.out, isOut = valueInterface.(sql.Out)
if isOut {
valueInterface, err = driver.DefaultParameterConverter.ConvertValue(sbind.out.Dest)
if err != nil {
binds = append(binds, sbind)
freeBinds(binds)
return nil, err
}
switch valueInterface.(type) {
case nil:
isNill = true
valueInterface = sbind.out.Dest
switch valueInterface.(type) {
case *sql.NullBool:
valueInterface = false
case *sql.NullFloat64:
valueInterface = float64(0)
case *sql.NullInt64:
valueInterface = int64(0)
case *sql.NullString:
valueInterface = ""
}
}
}
switch value := valueInterface.(type) {
case nil:
sbind.dataType = C.SQLT_AFC
sbind.pbuf = nil
sbind.maxSize = 0
*sbind.indicator = -1 // set to null
case []byte:
if isOut {
if len(value) > 32767 {
var lobP *unsafe.Pointer
lobP, _, err = stmt.conn.gciDescriptorAlloc(C.GCI_DTYPE_LOB, 0)
if err != nil {
freeBinds(binds)
return nil, err
}
sbind.dataType = C.SQLT_BLOB
sbind.pbuf = unsafe.Pointer(lobP)
sbind.maxSize = C.sb4(sizeOfNilPointer)
*sbind.length = C.ub2(sizeOfNilPointer)
lobLocator := (**C.GCILobLocator)(sbind.pbuf)
err = stmt.conn.gciLobCreateTemporary(*lobLocator, C.SQLCS_IMPLICIT, C.GCI_TEMP_BLOB)
if err != nil {
freeBinds(binds)
return nil, err
}
err = stmt.conn.gciLobWrite(*lobLocator, C.SQLCS_IMPLICIT, value)
if err != nil {
freeBinds(binds)
return nil, err
}
} else {
sbind.dataType = C.SQLT_BIN
sbind.pbuf = unsafe.Pointer(cByteN(value, 32768))
sbind.maxSize = 32767
if sbind.out.In && !isNill {
*sbind.length = C.ub2(len(value))
} else {
*sbind.indicator = -1 // set to null
}
}
} else {
if len(value) > 32767 {
var lobP *unsafe.Pointer
lobP, _, err = stmt.conn.gciDescriptorAlloc(C.GCI_DTYPE_LOB, 0)
if err != nil {
freeBinds(binds)
return nil, err
}
sbind.dataType = C.SQLT_BLOB
sbind.pbuf = unsafe.Pointer(lobP)
sbind.maxSize = C.sb4(sizeOfNilPointer)
*sbind.length = C.ub2(sizeOfNilPointer)
lobLocator := (**C.GCILobLocator)(sbind.pbuf)
err = stmt.conn.gciLobCreateTemporary(*lobLocator, C.SQLCS_IMPLICIT, C.GCI_TEMP_BLOB)
if err != nil {
freeBinds(binds)
return nil, err
}
err = stmt.conn.gciLobWrite(*lobLocator, C.SQLCS_IMPLICIT, value)
if err != nil {
freeBinds(binds)
return nil, err
}
} else {
sbind.dataType = C.SQLT_BIN
sbind.pbuf = unsafe.Pointer(cByte(value))
sbind.maxSize = C.sb4(len(value))
*sbind.length = C.ub2(len(value))
}
}
case time.Time:
sbind.dataType = C.SQLT_TIMESTAMP_TZ
sbind.maxSize = C.sb4(sizeOfNilPointer)
*sbind.length = C.ub2(sizeOfNilPointer)
dateTimePP, err := stmt.conn.timeToGCIDateTime(&value)
if err != nil {
freeBinds(binds)
return nil, fmt.Errorf("timeToGCIDateTime for column %v - error: %v", i, err)
}
sbind.pbuf = unsafe.Pointer(dateTimePP)
case string:
if isOut {
if len(value) > 32767 {
var lobP *unsafe.Pointer
lobP, _, err = stmt.conn.gciDescriptorAlloc(C.GCI_DTYPE_LOB, 0)
if err != nil {
freeBinds(binds)
return nil, err
}
sbind.dataType = C.SQLT_CLOB
sbind.pbuf = unsafe.Pointer(lobP)
sbind.maxSize = C.sb4(sizeOfNilPointer)
*sbind.length = C.ub2(sizeOfNilPointer)
lobLocator := (**C.GCILobLocator)(sbind.pbuf)
err = stmt.conn.gciLobCreateTemporary(*lobLocator, C.SQLCS_IMPLICIT, C.GCI_TEMP_CLOB)
if err != nil {
freeBinds(binds)
return nil, err
}
err = stmt.conn.gciLobWrite(*lobLocator, C.SQLCS_IMPLICIT, []byte(value))
if err != nil {
freeBinds(binds)
return nil, err
}
} else {
sbind.dataType = C.SQLT_CHR
sbind.pbuf = unsafe.Pointer(cStringN(value, 32768))
sbind.maxSize = 32767
if sbind.out.In && !isNill {
*sbind.length = C.ub2(len(value))
} else {
*sbind.indicator = -1 // set to null
}
}
} else {
if len(value) > 32767 {
var lobP *unsafe.Pointer
lobP, _, err = stmt.conn.gciDescriptorAlloc(C.GCI_DTYPE_LOB, 0)
if err != nil {
freeBinds(binds)
return nil, err
}
sbind.dataType = C.SQLT_CLOB
sbind.pbuf = unsafe.Pointer(lobP)
sbind.maxSize = C.sb4(sizeOfNilPointer)
*sbind.length = C.ub2(sizeOfNilPointer)
lobLocator := (**C.GCILobLocator)(sbind.pbuf)
err = stmt.conn.gciLobCreateTemporary(*lobLocator, C.SQLCS_IMPLICIT, C.GCI_TEMP_CLOB)
if err != nil {
freeBinds(binds)
return nil, err
}
err = stmt.conn.gciLobWrite(*lobLocator, C.SQLCS_IMPLICIT, []byte(value))
if err != nil {
freeBinds(binds)
return nil, err
}
} else {
sbind.dataType = C.SQLT_AFC
sbind.pbuf = unsafe.Pointer(C.CString(value))
sbind.maxSize = C.sb4(len(value))
*sbind.length = C.ub2(len(value))
}
}
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr:
buffer := bytes.Buffer{}
err = binary.Write(&buffer, binary.LittleEndian, value)
if err != nil {
freeBinds(binds)
return nil, fmt.Errorf("binary read for column %v - error: %v", i, err)
}
sbind.dataType = C.SQLT_INT
sbind.pbuf = unsafe.Pointer(cByte(buffer.Bytes()))
sbind.maxSize = C.sb4(buffer.Len())
*sbind.length = C.ub2(buffer.Len())
if isOut && sbind.out.In && isNill {
*sbind.indicator = -1 // set to null
}
case float32, float64:
buffer := bytes.Buffer{}
err = binary.Write(&buffer, binary.LittleEndian, value)
if err != nil {
freeBinds(binds)
return nil, fmt.Errorf("binary read for column %v - error: %v", i, err)
}
sbind.dataType = C.SQLT_BDOUBLE
sbind.pbuf = unsafe.Pointer(cByte(buffer.Bytes()))
sbind.maxSize = C.sb4(buffer.Len())
*sbind.length = C.ub2(buffer.Len())
if isOut && sbind.out.In && isNill {
*sbind.indicator = -1 // set to null
}
case bool:
sbind.dataType = C.SQLT_INT
if value {
sbind.pbuf = unsafe.Pointer(cByte([]byte{1}))
} else {
sbind.pbuf = unsafe.Pointer(cByte([]byte{0}))
}
sbind.maxSize = 1
*sbind.length = 1
if isOut && sbind.out.In && isNill {
*sbind.indicator = -1 // set to null
}
default:
if isOut {
// TODO: should this error instead of setting to null?
sbind.dataType = C.SQLT_AFC
sbind.pbuf = nil
sbind.maxSize = 0
*sbind.length = 0
*sbind.indicator = -1 // set to null
} else {
d := fmt.Sprintf("%v", value)
sbind.dataType = C.SQLT_AFC
sbind.pbuf = unsafe.Pointer(C.CString(d))
sbind.maxSize = C.sb4(len(d))
*sbind.length = C.ub2(len(d))
}
}
// add to binds now so if error will be freed by freeBinds call
binds = append(binds, sbind)
if useValues || len(namedValues[i].Name) < 1 {
err = stmt.gciBindByPos(C.ub4(i+1), &sbind)
// TODO: should we use namedValues[i]Ordinal?
} else {
err = stmt.gciBindByName([]byte(":"+namedValues[i].Name), &sbind)
}
if err != nil {
freeBinds(binds)
return nil, err
}
}
return binds, nil
}
// Query runs a query
func (stmt *Stmt) Query(values []driver.Value) (driver.Rows, error) {
stmt.ctx = context.Background()
binds, err := stmt.bindValues(values, nil)
if err != nil {
return nil, err
}
return stmt.query(binds)
}
// QueryContext runs a query with context
func (stmt *Stmt) QueryContext(ctx context.Context, namedValues []driver.NamedValue) (driver.Rows, error) {
stmt.ctx = ctx
binds, err := stmt.bindValues(nil, namedValues)
if err != nil {
return nil, err
}
return stmt.query(binds)
}
// query runs a query with context
func (stmt *Stmt) query(binds []bindStruct) (driver.Rows, error) {
defer freeBinds(binds)
var stmtType C.ub2
_, err := stmt.gciAttrGet(unsafe.Pointer(&stmtType), C.GCI_ATTR_STMT_TYPE)
if err != nil {
return nil, err
}
iter := C.ub4(1)
if stmtType == C.GCI_STMT_SELECT {
iter = 0
}
if stmt.conn.prefetchRows != 1 {
prefetchRows := stmt.conn.prefetchRows
// GCI_ATTR_PREFETCH_ROWS sets the number of top level rows to be prefetched. The default value is 1 row. Value of 0 seems to mean only prefetch memory size limits the number of rows to prefetch.
err = stmt.conn.gciAttrSet(unsafe.Pointer(stmt.stmt), C.GCI_HTYPE_STMT, unsafe.Pointer(&prefetchRows), 0, C.GCI_ATTR_PREFETCH_ROWS)
if err != nil {
return nil, err
}
}
if stmt.conn.prefetchMemory > 0 {
prefetchMemory := stmt.conn.prefetchMemory
// GCI_ATTR_PREFETCH_MEMORY sets the memory level for top level rows to be prefetched. Rows up to the specified top level row count are fetched if it occupies no more than the specified memory usage limit.
// The default value is 0, which means that memory size is not included in computing the number of rows to prefetch.
err = stmt.conn.gciAttrSet(unsafe.Pointer(stmt.stmt), C.GCI_HTYPE_STMT, unsafe.Pointer(&prefetchMemory), 0, C.GCI_ATTR_PREFETCH_MEMORY)
if err != nil {
return nil, err
}
}
mode := C.ub4(C.GCI_DEFAULT)
if !stmt.conn.inTransaction {
mode = mode | C.GCI_COMMIT_ON_SUCCESS
}
if stmt.ctx.Err() != nil {
return nil, stmt.ctx.Err()
}
done := make(chan struct{})
go stmt.conn.gciBreakDone(stmt.ctx, done)
err = stmt.gciStmtExecute(iter, mode)
close(done)
if err != nil {
return nil, err
}
var defines []defineStruct
defines, err = stmt.makeDefines()
if err != nil {
return nil, err
}
if stmt.ctx.Err() != nil {
freeDefines(defines)
return nil, stmt.ctx.Err()
}
rows := &Rows{
stmt: stmt,
defines: defines,
}
return rows, nil
}
func (stmt *Stmt) makeDefines() ([]defineStruct, error) {
var paramCountUb4 C.ub4 // number of columns in the select-list
_, err := stmt.gciAttrGet(unsafe.Pointer(&paramCountUb4), C.GCI_ATTR_PARAM_COUNT)
if err != nil {
return nil, err
}
paramCount := int(paramCountUb4)
defines := make([]defineStruct, paramCount)
for i := 0; i < paramCount; i++ {
if stmt.ctx.Err() != nil {
freeDefines(defines)
return nil, stmt.ctx.Err()
}
var param *C.GCIParam
param, err = stmt.gciParamGet(C.ub4(i + 1))
if err != nil {
freeDefines(defines)
return nil, err
}
defer C.GCIDescriptorFree(unsafe.Pointer(param), C.GCI_DTYPE_PARAM)
var dataType C.ub2 // external datatype of the column:
_, err = stmt.conn.gciAttrGet(param, unsafe.Pointer(&dataType), C.GCI_ATTR_DATA_TYPE)
if err != nil {
freeDefines(defines)
return nil, err
}
var columnName *C.GCItext // name of the column
var size C.ub4
size, err = stmt.conn.gciAttrGet(param, unsafe.Pointer(&columnName), C.GCI_ATTR_NAME)
if err != nil {
freeDefines(defines)
return nil, err
}
defines[i].name = cGoStringN(columnName, int(size))
var maxSize C.ub4 // Maximum size in bytes of the external data for the column. This can affect conversion buffer sizes.
_, err = stmt.conn.gciAttrGet(param, unsafe.Pointer(&maxSize), C.GCI_ATTR_DATA_SIZE)
if err != nil {
freeDefines(defines)
return nil, err
}
defines[i].length = (*C.ub2)(C.malloc(C.sizeof_ub2))
*defines[i].length = 0
defines[i].indicator = (*C.sb2)(C.malloc(C.sizeof_sb2))
*defines[i].indicator = 0
// switch on dataType
switch dataType {
case C.SQLT_AFC, C.SQLT_CHR, C.SQLT_VCS, C.SQLT_AVC:
defines[i].dataType = C.SQLT_AFC
// For a database with character set to ZHS16GBK the GCI C driver does not seem to report the correct max size, not sure exactly why.
// Doubling the max size of the buffer seems to fix the issue, not sure if there is a better fix.
defines[i].maxSize = C.sb4(maxSize * 2)
defines[i].pbuf = C.malloc(C.size_t(defines[i].maxSize))
case C.SQLT_BIN:
defines[i].dataType = C.SQLT_BIN
defines[i].maxSize = C.sb4(maxSize)
defines[i].pbuf = C.malloc(C.size_t(defines[i].maxSize))
case C.SQLT_NUM:
var precision C.sb2 // the precision
_, err = stmt.conn.gciAttrGet(param, unsafe.Pointer(&precision), C.GCI_ATTR_PRECISION)
if err != nil {
freeDefines(defines)
return nil, err
}
var scale C.sb1 // the scale (number of digits to the right of the decimal point)
_, err = stmt.conn.gciAttrGet(param, unsafe.Pointer(&scale), C.GCI_ATTR_SCALE)
if err != nil {
freeDefines(defines)
return nil, err
}
// The precision of numeric type attributes. If the precision is nonzero and scale is -127, then it is a FLOAT;
// otherwise, it is a NUMBER(precision, scale).
// When precision is 0, NUMBER(precision, scale) can be represented simply as NUMBER.
// note that select sum and count both return as precision == 0 && scale == 0 so use float64 (SQLT_BDOUBLE) to handle both
if (precision == 0 && scale == 0) || scale > 0 || scale == -127 {
defines[i].dataType = C.SQLT_BDOUBLE
defines[i].maxSize = 8
defines[i].pbuf = C.malloc(C.size_t(defines[i].maxSize))
} else {
defines[i].dataType = C.SQLT_INT
defines[i].maxSize = 8
defines[i].pbuf = C.malloc(C.size_t(defines[i].maxSize))
}
case C.SQLT_INT:
defines[i].dataType = C.SQLT_INT
defines[i].maxSize = 8
defines[i].pbuf = C.malloc(C.size_t(defines[i].maxSize))
case C.SQLT_FLT:
defines[i].dataType = C.SQLT_BDOUBLE
defines[i].maxSize = 8
defines[i].pbuf = C.malloc(C.size_t(defines[i].maxSize))
case C.SQLT_BDOUBLE, C.SQLT_IBDOUBLE, C.SQLT_BFLOAT, C.SQLT_IBFLOAT:
defines[i].dataType = C.SQLT_BDOUBLE
defines[i].maxSize = 8
defines[i].pbuf = C.malloc(C.size_t(defines[i].maxSize))
case C.SQLT_LNG:
defines[i].dataType = C.SQLT_LNG
defines[i].maxSize = 4000
defines[i].pbuf = C.malloc(C.size_t(defines[i].maxSize))
case C.SQLT_CLOB, C.SQLT_BLOB:
defines[i].dataType = dataType
defines[i].maxSize = C.sb4(sizeOfNilPointer)
var lobP *unsafe.Pointer
lobP, _, err = stmt.conn.gciDescriptorAlloc(C.GCI_DTYPE_LOB, 0)
if err != nil {
freeDefines(defines)
return nil, err
}
defines[i].pbuf = unsafe.Pointer(lobP)
case C.SQLT_TIMESTAMP, C.SQLT_DAT:
defines[i].dataType = C.SQLT_TIMESTAMP
defines[i].maxSize = C.sb4(sizeOfNilPointer)
var timestampP *unsafe.Pointer
timestampP, _, err = stmt.conn.gciDescriptorAlloc(C.GCI_DTYPE_TIMESTAMP, 0)
if err != nil {
freeDefines(defines)
return nil, err
}
defines[i].pbuf = unsafe.Pointer(timestampP)
case C.SQLT_TIMESTAMP_TZ, C.SQLT_TIMESTAMP_LTZ:
defines[i].dataType = C.SQLT_TIMESTAMP_TZ
defines[i].maxSize = C.sb4(sizeOfNilPointer)
var timestampP *unsafe.Pointer
timestampP, _, err = stmt.conn.gciDescriptorAlloc(C.GCI_DTYPE_TIMESTAMP_TZ, 0)
if err != nil {
freeDefines(defines)
return nil, err
}
defines[i].pbuf = unsafe.Pointer(timestampP)
case C.SQLT_INTERVAL_DS:
defines[i].dataType = C.SQLT_INTERVAL_DS
defines[i].maxSize = C.sb4(sizeOfNilPointer)
var intervalP *unsafe.Pointer
intervalP, _, err = stmt.conn.gciDescriptorAlloc(C.GCI_DTYPE_INTERVAL_DS, 0)
if err != nil {
freeDefines(defines)
return nil, err
}
defines[i].pbuf = unsafe.Pointer(intervalP)
case C.SQLT_INTERVAL_YM:
defines[i].dataType = C.SQLT_INTERVAL_YM
defines[i].maxSize = C.sb4(sizeOfNilPointer)
var intervalP *unsafe.Pointer
intervalP, _, err = stmt.conn.gciDescriptorAlloc(C.GCI_DTYPE_INTERVAL_YM, 0)
if err != nil {
freeDefines(defines)
return nil, err
}
defines[i].pbuf = unsafe.Pointer(intervalP)
case C.SQLT_RDD: // rowid
defines[i].dataType = C.SQLT_AFC
defines[i].maxSize = 40
defines[i].pbuf = C.malloc(C.size_t(defines[i].maxSize))
case C.SQLT_RSET: // ref cursor
defines[i].dataType = dataType
defines[i].maxSize = C.sb4(sizeOfNilPointer)
var stmtP *unsafe.Pointer
stmtP, _, err = stmt.conn.gciHandleAlloc(C.GCI_HTYPE_STMT, 0)
if err != nil {
freeDefines(defines)
return nil, err
}
defines[i].pbuf = unsafe.Pointer(stmtP)
default:
defines[i].dataType = C.SQLT_AFC
defines[i].maxSize = C.sb4(maxSize)
defines[i].pbuf = C.malloc(C.size_t(defines[i].maxSize))
}
result := C.GCIDefineByPos(
stmt.stmt, // statement handle
&defines[i].defineHandle, // pointer to a pointer to a define handle. If NULL, this call implicitly allocates the define handle.
stmt.conn.errHandle, // error handle
C.ub4(i+1), // position of this value in the select list. Positions are 1-based and are numbered from left to right.
defines[i].pbuf, // pointer to a buffer
defines[i].maxSize, // size of each valuep buffer in bytes
defines[i].dataType, // datatype
unsafe.Pointer(defines[i].indicator), // pointer to an indicator variable or array
defines[i].length, // pointer to array of length of data fetched
nil, // pointer to array of column-level return codes
C.GCI_DEFAULT, // mode - GCI_DEFAULT - This is the default mode.
)
if result != C.GCI_SUCCESS {
freeDefines(defines)
return nil, stmt.conn.getError(result)
}
}
return defines, nil
}
// getRowid returns the rowid
func (stmt *Stmt) getRowid() (string, error) {
rowidP, _, err := stmt.conn.gciDescriptorAlloc(C.GCI_DTYPE_ROWID, 0)
if err != nil {
return "", err
}
defer C.GCIDescriptorFree(*rowidP, C.GCI_DTYPE_ROWID)
// GCI_ATTR_ROWID returns the ROWID descriptor allocated with GCIDescriptorAlloc()
_, err = stmt.gciAttrGet(*rowidP, C.GCI_ATTR_ROWID)
if err != nil {
return "", err
}
rowid := cStringN("", 18)
defer C.free(unsafe.Pointer(rowid))
rowidLength := C.ub2(18)
result := C.GCIRowidToChar((*C.GCIRowid)(*rowidP), rowid, &rowidLength, stmt.conn.errHandle)
err = stmt.conn.getError(result)
if err != nil {
return "", err
}
return cGoStringN(rowid, int(rowidLength)), nil
}
// rowsAffected returns the number of rows affected
func (stmt *Stmt) rowsAffected() (int64, error) {
var rowCount C.ub4 // Number of rows processed so far after SELECT statements. For INSERT, UPDATE, and DELETE statements, it is the number of rows processed by the most recent statement. The default value is 1.
_, err := stmt.gciAttrGet(unsafe.Pointer(&rowCount), C.GCI_ATTR_ROW_COUNT)
if err != nil {
return -1, err
}
return int64(rowCount), nil
}
// Exec runs an exec query
func (stmt *Stmt) Exec(values []driver.Value) (driver.Result, error) {
stmt.ctx = context.Background()
binds, err := stmt.bindValues(values, nil)
if err != nil {
return nil, err
}
return stmt.exec(binds)
}
// ExecContext run a exec query with context
func (stmt *Stmt) ExecContext(ctx context.Context, namedValues []driver.NamedValue) (driver.Result, error) {
stmt.ctx = ctx
binds, err := stmt.bindValues(nil, namedValues)
if err != nil {
return nil, err
}
return stmt.exec(binds)
}
func (stmt *Stmt) exec(binds []bindStruct) (driver.Result, error) {
defer freeBinds(binds)
mode := C.ub4(C.GCI_DEFAULT)
if stmt.conn.inTransaction == false {
mode = mode | C.GCI_COMMIT_ON_SUCCESS
}
if stmt.ctx.Err() != nil {
return nil, stmt.ctx.Err()
}
done := make(chan struct{})
go stmt.conn.gciBreakDone(stmt.ctx, done)
err := stmt.gciStmtExecute(1, mode)
close(done)
if err != nil && err != ErrGCISuccessWithInfo {
return nil, err
}
result := Result{stmt: stmt}
result.rowsAffected, result.rowsAffectedErr = stmt.rowsAffected()
if result.rowsAffectedErr != nil || result.rowsAffected < 1 {
result.rowidErr = ErrNoRowid
} else {
result.rowid, result.rowidErr = stmt.getRowid()
}
err = stmt.outputBoundParameters(binds)
if err != nil {
return nil, err
}
return &result, nil
}
// outputBoundParameters sets bound parameters
func (stmt *Stmt) outputBoundParameters(binds []bindStruct) error {
var err error
for i, bind := range binds {
if bind.pbuf != nil {
switch dest := bind.out.Dest.(type) {
case *string:
switch {
case *bind.indicator > 0: // indicator variable is the actual length before truncation
spaces := int(*bind.indicator) - int(*bind.length)
if spaces < 0 {
return fmt.Errorf("spaces less than 0 for column %v", i)
}
*dest = C.GoStringN((*C.char)(bind.pbuf), C.int(*bind.length)) + strings.Repeat(" ", spaces)
case *bind.indicator == 0: // Normal
if bind.dataType == C.SQLT_CLOB {
lobLocator := (**C.GCILobLocator)(bind.pbuf)
var buffer []byte
buffer, err = stmt.conn.gciLobRead(*lobLocator, C.SQLCS_IMPLICIT)
if err != nil {
return err
}
*dest = string(buffer)
} else {
*dest = C.GoStringN((*C.char)(bind.pbuf), C.int(*bind.length))
}
case *bind.indicator == -1: // The selected value is null
*dest = "" // best attempt at Go nil string
case *bind.indicator == -2: // Item is greater than the length of the output variable; the item has been truncated.
*dest = C.GoStringN((*C.char)(bind.pbuf), C.int(*bind.length))
// TODO: should this be an error?
default:
return fmt.Errorf("unknown column indicator %d for column %v", *bind.indicator, i)
}
case *sql.NullString:
switch {
case *bind.indicator > 0: // indicator variable is the actual length before truncation
spaces := int(*bind.indicator) - int(*bind.length)
if spaces < 0 {
return fmt.Errorf("spaces less than 0 for column %v", i)
}
dest.String = C.GoStringN((*C.char)(bind.pbuf), C.int(*bind.length)) + strings.Repeat(" ", spaces)
dest.Valid = true
case *bind.indicator == 0: // Normal
dest.String = C.GoStringN((*C.char)(bind.pbuf), C.int(*bind.length))
dest.Valid = true
case *bind.indicator == -1: // The selected value is null
dest.String = ""
dest.Valid = false
case *bind.indicator == -2: // Item is greater than the length of the output variable; the item has been truncated.
dest.String = C.GoStringN((*C.char)(bind.pbuf), C.int(*bind.length))
dest.Valid = true
// TODO: should this be an error?
default:
return fmt.Errorf("unknown column indicator %d for column %v", *bind.indicator, i)
}
case *int:
*dest = int(getInt64(bind.pbuf))
case *int64:
*dest = getInt64(bind.pbuf)
case *int32:
*dest = int32(getInt64(bind.pbuf))
case *int16:
*dest = int16(getInt64(bind.pbuf))
case *int8:
*dest = int8(getInt64(bind.pbuf))
case *sql.NullInt64:
if *bind.indicator == -1 {
dest.Int64 = 0
dest.Valid = false
} else {
dest.Int64 = getInt64(bind.pbuf)
dest.Valid = true
}
case *uint:
*dest = uint(getUint64(bind.pbuf))
case *uint64:
*dest = getUint64(bind.pbuf)
case *uint32:
*dest = uint32(getUint64(bind.pbuf))
case *uint16:
*dest = uint16(getUint64(bind.pbuf))
case *uint8:
*dest = uint8(getUint64(bind.pbuf))
case *uintptr:
*dest = uintptr(getUint64(bind.pbuf))
case *float64:
buf := (*[8]byte)(bind.pbuf)[0:8]
var data float64
err = binary.Read(bytes.NewReader(buf), binary.LittleEndian, &data)
if err != nil {
return fmt.Errorf("binary read for column %v - error: %v", i, err)
}
*dest = data
case *float32:
// statement is using SQLT_BDOUBLE to bind
// need to read as float64 because of the 8 bits
buf := (*[8]byte)(bind.pbuf)[0:8]
var data float64
err = binary.Read(bytes.NewReader(buf), binary.LittleEndian, &data)
if err != nil {
return fmt.Errorf("binary read for column %v - error: %v", i, err)
}
*dest = float32(data)
case *sql.NullFloat64:
if *bind.indicator == -1 {
dest.Float64 = 0
dest.Valid = false
} else {
buf := (*[8]byte)(bind.pbuf)[0:8]
var data float64
err = binary.Read(bytes.NewReader(buf), binary.LittleEndian, &data)
if err != nil {
return fmt.Errorf("binary read for column %v - error: %v", i, err)
}
dest.Float64 = data
dest.Valid = true
}
case *bool:
buf := (*[1 << 30]byte)(bind.pbuf)[0:1]
*dest = buf[0] != 0
case *sql.NullBool:
if *bind.indicator == -1 {
dest.Bool = false
dest.Valid = false
} else {
buf := (*[1 << 30]byte)(bind.pbuf)[0:1]
dest.Bool = buf[0] != 0
dest.Valid = true
}
case *[]byte:
switch {
case *bind.indicator > 0: // indicator variable is the actual length before truncation
if int(*bind.indicator)-int(*bind.length) < 0 {
return fmt.Errorf("spaces less than 0 for column %v", i)
}
*dest = C.GoBytes(bind.pbuf, C.int(*bind.indicator))
case *bind.indicator == 0: // Normal
if bind.dataType == C.SQLT_BLOB {
lobLocator := (**C.GCILobLocator)(bind.pbuf)
*dest, err = stmt.conn.gciLobRead(*lobLocator, C.SQLCS_IMPLICIT)
if err != nil {
return err
}
} else {
*dest = C.GoBytes(bind.pbuf, C.int(*bind.length))
}
case *bind.indicator == -1: // The selected value is null
*dest = nil
case *bind.indicator == -2: // Item is greater than the length of the output variable; the item has been truncated.
*dest = C.GoBytes(bind.pbuf, C.int(*bind.length))
// TODO: should this be an error?
default:
return fmt.Errorf("unknown column indicator %d for column %v", *bind.indicator, i)
}
}
}
}
return nil
}
// gciParamGet calls GCIParamGet then returns GCIParam and error.
// GCIDescriptorFree must be called on returned GCIParam.
func (stmt *Stmt) gciParamGet(position C.ub4) (*C.GCIParam, error) {
var paramTemp *C.GCIParam
param := &paramTemp
result := C.GCIParamGet(
unsafe.Pointer(stmt.stmt), // A statement handle or describe handle
C.GCI_HTYPE_STMT, // Handle type: GCI_HTYPE_STMT, for a statement handle
stmt.conn.errHandle, // An error handle
(*unsafe.Pointer)(unsafe.Pointer(param)), // A descriptor of the parameter at the position
position, // Position number in the statement handle or describe handle. A parameter descriptor will be returned for this position.
)
err := stmt.conn.getError(result)
if err != nil {
return nil, err
}
return *param, nil
}
// gciAttrGet calls GCIAttrGet with GCIStmt then returns attribute size and error.
// The attribute value is stored into passed value.
func (stmt *Stmt) gciAttrGet(value unsafe.Pointer, attributeType C.ub4) (C.ub4, error) {
var size C.ub4
result := C.GCIAttrGet(
unsafe.Pointer(stmt.stmt), // Pointer to a handle type
C.GCI_HTYPE_STMT, // The handle type: GCI_HTYPE_STMT, for a statement handle
value, // Pointer to the storage for an attribute value
&size, // The size of the attribute value
attributeType, // The attribute type
stmt.conn.errHandle, // An error handle
)
return size, stmt.conn.getError(result)
}
// gciBindByName calls GCIBindByName, then returns bind handle and error.
func (stmt *Stmt) gciBindByName(name []byte, bind *bindStruct) error {
result := C.GCIBindByName(
stmt.stmt, // The statement handle
&bind.bindHandle, // The bind handle that is implicitly allocated by this call. The handle is freed implicitly when the statement handle is deallocated.
stmt.conn.errHandle, // An error handle
(*C.GCItext)(&name[0]), // The placeholder, specified by its name, that maps to a variable in the statement assgciated with the statement handle.
C.sb4(len(name)), // The length of the name specified in placeholder, in number of bytes regardless of the encoding.
bind.pbuf, // The pointer to a data value or an array of data values of type specified in the dty parameter
bind.maxSize, // The maximum size possible in bytes of any data value for this bind variable
bind.dataType, // The data type of the values being bound
unsafe.Pointer(bind.indicator), // Pointer to an indicator variable or array
bind.length, // lengths are in bytes in general
nil, // Pointer to the array of column-level return codes
0, // A maximum array length parameter
nil, // Current array length parameter
C.GCI_DEFAULT, // The mode. Recommended to set to GCI_DEFAULT, which makes the bind variable have the same encoding as its statement.
)
return stmt.conn.getError(result)
}
// gciBindByPos calls GCIBindByPos, then returns bind handle and error.
func (stmt *Stmt) gciBindByPos(position C.ub4, bind *bindStruct) error {
result := C.GCIBindByPos(
stmt.stmt, // The statement handle
&bind.bindHandle, // The bind handle that is implicitly allocated by this call. The handle is freed implicitly when the statement handle is deallocated.
stmt.conn.errHandle, // An error handle
position, // The placeholder attributes are specified by position if GCIBindByPos() is being called.
bind.pbuf, // An address of a data value or an array of data values
bind.maxSize, // The maximum size possible in bytes of any data value for this bind variable
bind.dataType, // The data type of the values being bound
unsafe.Pointer(bind.indicator), // Pointer to an indicator variable or array
bind.length, // lengths are in bytes in general
nil, // Pointer to the array of column-level return codes
0, // A maximum array length parameter
nil, // Current array length parameter
C.GCI_DEFAULT, // The mode. Recommended to set to GCI_DEFAULT, which makes the bind variable have the same encoding as its statement.
)
return stmt.conn.getError(result)
}
// gciStmtExecute calls GCIStmtExecute
func (stmt *Stmt) gciStmtExecute(iters C.ub4, mode C.ub4) error {
result := C.GCIStmtExecute(
stmt.conn.svc, // Service context handle
stmt.stmt, // A statement handle
stmt.conn.errHandle, // An error handle
iters, // For non-SELECT statements, the number of times this statement is executed equals iters - rowoff. For SELECT statements, if iters is nonzero, then defines must have been done for the statement handle.
0, // The starting index from which the data in an array bind is relevant for this multiple row execution
nil, // This parameter is optional. If it is supplied, it must point to a snapshot descriptor of type GCI_DTYPE_SNAP
nil, // This parameter is optional. If it is supplied, it must point to a descriptor of type GCI_DTYPE_SNAP.
mode, // The mode
)
if stmt.cacheKey != "" && result != C.GCI_SUCCESS && result != C.GCI_SUCCESS_WITH_INFO {
// drop statement from cache for all errors when caching is enabled
stmt.releaseMode = C.GCI_STRLS_CACHE_DELETE
}
return stmt.conn.getError(result)
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Go
1
https://gitee.com/GBase8s/go-gci.git
git@gitee.com:GBase8s/go-gci.git
GBase8s
go-gci
go-gci
master

搜索帮助

D67c1975 1850385 1daf7b77 1850385