func (s *OCI8Stmt) bind(args []driver.Value) (boundParameters []oci8bind, err error) { if args == nil { return nil, nil } var ( dty C.ub2 cdata *C.char clen C.sb4 ) *s.bp = nil for i, v := range args { switch v.(type) { case nil: dty = C.SQLT_STR cdata = nil clen = 0 case []byte: v := v.([]byte) dty = C.SQLT_BIN cdata = CByte(v) clen = C.sb4(len(v)) boundParameters = append(boundParameters, oci8bind{dty, unsafe.Pointer(cdata)}) case float64: fb := math.Float64bits(v.(float64)) if fb&0x8000000000000000 != 0 { fb ^= 0xffffffffffffffff } else { fb |= 0x8000000000000000 } dty = C.SQLT_IBDOUBLE cdata = CByte([]byte{byte(fb >> 56), byte(fb >> 48), byte(fb >> 40), byte(fb >> 32), byte(fb >> 24), byte(fb >> 16), byte(fb >> 8), byte(fb)}) clen = 8 boundParameters = append(boundParameters, oci8bind{dty, unsafe.Pointer(cdata)}) case time.Time: var pt unsafe.Pointer var zp unsafe.Pointer now := v.(time.Time) _, offset := now.Zone() sign := '+' if offset < 0 { offset = -offset sign = '-' } offset /= 60 // oracle accepts zones "[+-]hh:mm" zone := fmt.Sprintf("%c%02d:%02d", sign, offset/60, offset%60) zoneTxt := now.Location().String() size := len(zoneTxt) if size < 8 { size = 8 } size += int(unsafe.Sizeof(unsafe.Pointer(nil))) if ret := C.WrapOCIDescriptorAlloc( s.c.env, C.OCI_DTYPE_TIMESTAMP_TZ, C.size_t(size)); ret.rv != C.OCI_SUCCESS { defer freeBoundParameters(boundParameters) return nil, ociGetError(s.c.err) } else { dty = C.SQLT_TIMESTAMP_TZ clen = C.sb4(unsafe.Sizeof(pt)) pt = ret.extra *(*unsafe.Pointer)(ret.extra) = ret.ptr zp = unsafe.Pointer(uintptr(ret.extra) + unsafe.Sizeof(unsafe.Pointer(nil))) boundParameters = append(boundParameters, oci8bind{dty, pt}) } for first := true; ; first = false { copy((*[1 << 30]byte)(zp)[0:len(zone)], zone) rv := C.OCIDateTimeConstruct( s.c.env, (*C.OCIError)(s.c.err), (*C.OCIDateTime)(*(*unsafe.Pointer)(pt)), C.sb2(now.Year()), C.ub1(now.Month()), C.ub1(now.Day()), C.ub1(now.Hour()), C.ub1(now.Minute()), C.ub1(now.Second()), C.ub4(now.Nanosecond()), (*C.OraText)(zp), C.size_t(len(zone)), ) if rv != C.OCI_SUCCESS { if !first { defer freeBoundParameters(boundParameters) return nil, ociGetError(s.c.err) } zone = zoneTxt } else { break } } cdata = (*C.char)(pt) case string: v := v.(string) dty = C.SQLT_AFC // don't trim strings !!! cdata = C.CString(v) clen = C.sb4(len(v)) boundParameters = append(boundParameters, oci8bind{dty, unsafe.Pointer(cdata)}) case int64: val := v.(int64) dty = C.SQLT_INT clen = C.sb4(8) // not tested on i386. may only work on amd64 cdata = (*C.char)(C.malloc(8)) buf := (*[1 << 30]byte)(unsafe.Pointer(cdata))[0:8] buf[0] = byte(val & 0x0ff) buf[1] = byte(val >> 8 & 0x0ff) buf[2] = byte(val >> 16 & 0x0ff) buf[3] = byte(val >> 24 & 0x0ff) buf[4] = byte(val >> 32 & 0x0ff) buf[5] = byte(val >> 40 & 0x0ff) buf[6] = byte(val >> 48 & 0x0ff) buf[7] = byte(val >> 56 & 0x0ff) boundParameters = append(boundParameters, oci8bind{dty, unsafe.Pointer(cdata)}) case bool: // oracle dont have bool, handle as 0/1 dty = C.SQLT_INT clen = C.sb4(1) cdata = (*C.char)(C.malloc(10)) if v.(bool) { *cdata = 1 } else { *cdata = 0 } boundParameters = append(boundParameters, oci8bind{dty, unsafe.Pointer(cdata)}) default: dty = C.SQLT_CHR d := fmt.Sprintf("%v", v) clen = C.sb4(len(d)) cdata = C.CString(d) boundParameters = append(boundParameters, oci8bind{dty, unsafe.Pointer(cdata)}) } if rv := C.OCIBindByPos( (*C.OCIStmt)(s.s), s.bp, (*C.OCIError)(s.c.err), C.ub4(i+1), unsafe.Pointer(cdata), clen, dty, nil, nil, nil, 0, nil, C.OCI_DEFAULT); rv != C.OCI_SUCCESS { defer freeBoundParameters(boundParameters) return nil, ociGetError(s.c.err) } } return boundParameters, nil }
func (s *OCI8Stmt) Query(args []driver.Value) (rows driver.Rows, err error) { var ( fbp []oci8bind ) if fbp, err = s.bind(args); err != nil { return nil, err } defer freeBoundParameters(fbp) iter := C.ub4(1) if retUb2 := C.WrapOCIAttrGetUb2(s.s, C.OCI_HTYPE_STMT, C.OCI_ATTR_STMT_TYPE, (*C.OCIError)(s.c.err)); retUb2.rv != C.OCI_SUCCESS { return nil, ociGetError(s.c.err) } else if retUb2.num == C.OCI_STMT_SELECT { iter = 0 } // set the row prefetch. Only one extra row per fetch will be returned unless this is set. if prefetch_size := C.ub4(s.c.attrs.Get("prefetch_rows").(int)); prefetch_size > 0 { if rv := C.WrapOCIAttrSetUb4(s.s, C.OCI_HTYPE_STMT, prefetch_size, C.OCI_ATTR_PREFETCH_ROWS, (*C.OCIError)(s.c.err)); rv != C.OCI_SUCCESS { return nil, ociGetError(s.c.err) } } // if non-zero, oci will fetch rows until the memory limit or row prefetch limit is hit. // useful for memory constrained systems if prefetch_memory := C.ub4(s.c.attrs.Get("prefetch_memory").(int64)); prefetch_memory > 0 { if rv := C.WrapOCIAttrSetUb4(s.s, C.OCI_HTYPE_STMT, prefetch_memory, C.OCI_ATTR_PREFETCH_MEMORY, (*C.OCIError)(s.c.err)); rv != C.OCI_SUCCESS { return nil, ociGetError(s.c.err) } } mode := C.ub4(C.OCI_DEFAULT) if !s.c.inTransaction { mode = mode | C.OCI_COMMIT_ON_SUCCESS } if rv := C.OCIStmtExecute( (*C.OCISvcCtx)(s.c.svc), (*C.OCIStmt)(s.s), (*C.OCIError)(s.c.err), iter, 0, nil, nil, mode); rv != C.OCI_SUCCESS { return nil, ociGetError(s.c.err) } var rc int if retUb2 := C.WrapOCIAttrGetUb2(s.s, C.OCI_HTYPE_STMT, C.OCI_ATTR_PARAM_COUNT, (*C.OCIError)(s.c.err)); retUb2.rv != C.OCI_SUCCESS { return nil, ociGetError(s.c.err) } else { rc = int(retUb2.num) } oci8cols := make([]oci8col, rc) for i := 0; i < rc; i++ { var p unsafe.Pointer var tp C.ub2 var lp C.ub2 if rp := C.WrapOCIParamGet(s.s, C.OCI_HTYPE_STMT, (*C.OCIError)(s.c.err), C.ub4(i+1)); rp.rv != C.OCI_SUCCESS { return nil, ociGetError(s.c.err) } else { p = rp.ptr } if tpr := C.WrapOCIAttrGetUb2(p, C.OCI_DTYPE_PARAM, C.OCI_ATTR_DATA_TYPE, (*C.OCIError)(s.c.err)); tpr.rv != C.OCI_SUCCESS { return nil, ociGetError(s.c.err) } else { tp = tpr.num } if nsr := C.WrapOCIAttrGetString(p, C.OCI_DTYPE_PARAM, C.OCI_ATTR_NAME, (*C.OCIError)(s.c.err)); nsr.rv != C.OCI_SUCCESS { return nil, ociGetError(s.c.err) } else { oci8cols[i].name = string((*[1 << 30]byte)(unsafe.Pointer(nsr.ptr))[0:int(nsr.size)]) } if lpr := C.WrapOCIAttrGetUb2(p, C.OCI_DTYPE_PARAM, C.OCI_ATTR_DATA_SIZE, (*C.OCIError)(s.c.err)); lpr.rv != C.OCI_SUCCESS { return nil, ociGetError(s.c.err) } else { lp = lpr.num } /* var ( defp *C.OCIDefine ) */ *s.defp = nil switch tp { case C.SQLT_CHR, C.SQLT_AFC, C.SQLT_VCS, C.SQLT_AVC: // TODO: transfer as clob, read all bytes in loop // lp *= 4 // utf8 enc oci8cols[i].kind = C.SQLT_CHR // tp oci8cols[i].size = int(lp) * 4 // utf8 enc oci8cols[i].pbuf = C.malloc(C.size_t(oci8cols[i].size) + 1) case C.SQLT_BIN: oci8cols[i].kind = C.SQLT_BIN oci8cols[i].size = int(lp) oci8cols[i].pbuf = C.malloc(C.size_t(oci8cols[i].size)) case C.SQLT_NUM: oci8cols[i].kind = C.SQLT_CHR oci8cols[i].size = int(lp * 4) oci8cols[i].pbuf = C.malloc(C.size_t(oci8cols[i].size) + 1) case C.SQLT_IBDOUBLE, C.SQLT_IBFLOAT: oci8cols[i].kind = C.SQLT_IBDOUBLE oci8cols[i].size = int(8) oci8cols[i].pbuf = C.malloc(8) case C.SQLT_CLOB, C.SQLT_BLOB: // allocate +io buffers + ub4 size := int(unsafe.Sizeof(unsafe.Pointer(nil)) + unsafe.Sizeof(C.ub4(0))) if oci8cols[i].size < blobBufSize { size += blobBufSize } else { size += oci8cols[i].size } if ret := C.WrapOCIDescriptorAlloc(s.c.env, C.OCI_DTYPE_LOB, C.size_t(size)); ret.rv != C.OCI_SUCCESS { return nil, ociGetError(s.c.err) } else { oci8cols[i].kind = tp oci8cols[i].size = int(unsafe.Sizeof(unsafe.Pointer(nil))) oci8cols[i].pbuf = ret.extra *(*unsafe.Pointer)(ret.extra) = ret.ptr } // testing // case C.SQLT_DAT: // // oci8cols[i].kind = C.SQLT_DAT // oci8cols[i].size = int(lp) // oci8cols[i].pbuf = C.malloc(C.size_t(lp)) // case C.SQLT_TIMESTAMP, C.SQLT_DAT: if ret := C.WrapOCIDescriptorAlloc(s.c.env, C.OCI_DTYPE_TIMESTAMP, C.size_t(unsafe.Sizeof(unsafe.Pointer(nil)))); ret.rv != C.OCI_SUCCESS { return nil, ociGetError(s.c.err) } else { oci8cols[i].kind = C.SQLT_TIMESTAMP oci8cols[i].size = int(unsafe.Sizeof(unsafe.Pointer(nil))) oci8cols[i].pbuf = ret.extra *(*unsafe.Pointer)(ret.extra) = ret.ptr } case C.SQLT_TIMESTAMP_TZ, C.SQLT_TIMESTAMP_LTZ: if ret := C.WrapOCIDescriptorAlloc(s.c.env, C.OCI_DTYPE_TIMESTAMP_TZ, C.size_t(unsafe.Sizeof(unsafe.Pointer(nil)))); ret.rv != C.OCI_SUCCESS { return nil, ociGetError(s.c.err) } else { oci8cols[i].kind = C.SQLT_TIMESTAMP_TZ oci8cols[i].size = int(unsafe.Sizeof(unsafe.Pointer(nil))) oci8cols[i].pbuf = ret.extra *(*unsafe.Pointer)(ret.extra) = ret.ptr } case C.SQLT_INTERVAL_DS: if ret := C.WrapOCIDescriptorAlloc(s.c.env, C.OCI_DTYPE_INTERVAL_DS, C.size_t(unsafe.Sizeof(unsafe.Pointer(nil)))); ret.rv != C.OCI_SUCCESS { return nil, ociGetError(s.c.err) } else { oci8cols[i].kind = C.SQLT_INTERVAL_DS oci8cols[i].size = int(unsafe.Sizeof(unsafe.Pointer(nil))) oci8cols[i].pbuf = ret.extra *(*unsafe.Pointer)(ret.extra) = ret.ptr } case C.SQLT_INTERVAL_YM: if ret := C.WrapOCIDescriptorAlloc(s.c.env, C.OCI_DTYPE_INTERVAL_YM, C.size_t(unsafe.Sizeof(unsafe.Pointer(nil)))); ret.rv != C.OCI_SUCCESS { return nil, ociGetError(s.c.err) } else { oci8cols[i].kind = C.SQLT_INTERVAL_YM oci8cols[i].size = int(unsafe.Sizeof(unsafe.Pointer(nil))) oci8cols[i].pbuf = ret.extra *(*unsafe.Pointer)(ret.extra) = ret.ptr } case C.SQLT_RDD: // rowid lp = 40 oci8cols[i].pbuf = C.malloc(C.size_t(lp) + 1) oci8cols[i].kind = C.SQLT_CHR // tp oci8cols[i].size = int(lp + 1) default: oci8cols[i].pbuf = C.malloc(C.size_t(lp) + 1) oci8cols[i].kind = C.SQLT_CHR // tp oci8cols[i].size = int(lp + 1) } if rv := C.OCIDefineByPos( (*C.OCIStmt)(s.s), s.defp, (*C.OCIError)(s.c.err), C.ub4(i+1), oci8cols[i].pbuf, C.sb4(oci8cols[i].size), oci8cols[i].kind, unsafe.Pointer(&oci8cols[i].ind), &oci8cols[i].rlen, nil, C.OCI_DEFAULT); rv != C.OCI_SUCCESS { return nil, ociGetError(s.c.err) } } return &OCI8Rows{s, oci8cols, false}, nil }
func (s *OCI8Stmt) bind(args []namedValue) (boundParameters []oci8bind, err error) { if len(args) == 0 { return nil, nil } var ( dty C.ub2 cdata *C.char clen C.sb4 ) *s.bp = nil for i, uv := range args { switch v := uv.Value.(type) { case nil: dty = C.SQLT_STR cdata = nil clen = 0 case []byte: dty = C.SQLT_BIN cdata = CByte(v) clen = C.sb4(len(v)) boundParameters = append(boundParameters, oci8bind{dty, unsafe.Pointer(cdata)}) case float64: fb := math.Float64bits(v) if fb&0x8000000000000000 != 0 { fb ^= 0xffffffffffffffff } else { fb |= 0x8000000000000000 } dty = C.SQLT_IBDOUBLE cdata = CByte([]byte{byte(fb >> 56), byte(fb >> 48), byte(fb >> 40), byte(fb >> 32), byte(fb >> 24), byte(fb >> 16), byte(fb >> 8), byte(fb)}) clen = 8 boundParameters = append(boundParameters, oci8bind{dty, unsafe.Pointer(cdata)}) case time.Time: var pt unsafe.Pointer var zp unsafe.Pointer zone, offset := v.Zone() size := len(zone) if size < 8 { size = 8 } size += int(unsafe.Sizeof(unsafe.Pointer(nil))) if ret := C.WrapOCIDescriptorAlloc( s.c.env, C.OCI_DTYPE_TIMESTAMP_TZ, C.size_t(size)); ret.rv != C.OCI_SUCCESS { defer freeBoundParameters(boundParameters) return nil, ociGetError(ret.rv, s.c.err) } else { dty = C.SQLT_TIMESTAMP_TZ clen = C.sb4(unsafe.Sizeof(pt)) pt = ret.extra *(*unsafe.Pointer)(ret.extra) = ret.ptr zp = unsafe.Pointer(uintptr(ret.extra) + unsafe.Sizeof(unsafe.Pointer(nil))) boundParameters = append(boundParameters, oci8bind{dty, pt}) } tryagain := false copy((*[1 << 30]byte)(zp)[0:len(zone)], zone) rv := C.OCIDateTimeConstruct( s.c.env, (*C.OCIError)(s.c.err), (*C.OCIDateTime)(*(*unsafe.Pointer)(pt)), C.sb2(v.Year()), C.ub1(v.Month()), C.ub1(v.Day()), C.ub1(v.Hour()), C.ub1(v.Minute()), C.ub1(v.Second()), C.ub4(v.Nanosecond()), (*C.OraText)(zp), C.size_t(len(zone)), ) if rv != C.OCI_SUCCESS { tryagain = true } else { //check if oracle timezone offset is same ? rvz := C.WrapOCIDateTimeGetTimeZoneNameOffset( (*C.OCIEnv)(s.c.env), (*C.OCIError)(s.c.err), (*C.OCIDateTime)(*(*unsafe.Pointer)(pt))) if rvz.rv != C.OCI_SUCCESS { defer freeBoundParameters(boundParameters) return nil, ociGetError(rvz.rv, s.c.err) } if offset != int(rvz.h)*60*60+int(rvz.m)*60 { //fmt.Println("oracle timezone offset dont match", zone, offset, int(rvz.h)*60*60+int(rvz.m)*60) tryagain = true } } if tryagain { sign := '+' if offset < 0 { offset = -offset sign = '-' } offset /= 60 // oracle accept zones "[+-]hh:mm", try second time zone = fmt.Sprintf("%c%02d:%02d", sign, offset/60, offset%60) copy((*[1 << 30]byte)(zp)[0:len(zone)], zone) rv := C.OCIDateTimeConstruct( s.c.env, (*C.OCIError)(s.c.err), (*C.OCIDateTime)(*(*unsafe.Pointer)(pt)), C.sb2(v.Year()), C.ub1(v.Month()), C.ub1(v.Day()), C.ub1(v.Hour()), C.ub1(v.Minute()), C.ub1(v.Second()), C.ub4(v.Nanosecond()), (*C.OraText)(zp), C.size_t(len(zone)), ) if rv != C.OCI_SUCCESS { defer freeBoundParameters(boundParameters) return nil, ociGetError(rv, s.c.err) } } cdata = (*C.char)(pt) case string: dty = C.SQLT_AFC // don't trim strings !!! cdata = C.CString(v) clen = C.sb4(len(v)) boundParameters = append(boundParameters, oci8bind{dty, unsafe.Pointer(cdata)}) case int64: dty = C.SQLT_INT clen = C.sb4(8) // not tested on i386. may only work on amd64 cdata = (*C.char)(C.malloc(8)) buf := (*[1 << 30]byte)(unsafe.Pointer(cdata))[0:8] buf[0] = byte(v & 0x0ff) buf[1] = byte(v >> 8 & 0x0ff) buf[2] = byte(v >> 16 & 0x0ff) buf[3] = byte(v >> 24 & 0x0ff) buf[4] = byte(v >> 32 & 0x0ff) buf[5] = byte(v >> 40 & 0x0ff) buf[6] = byte(v >> 48 & 0x0ff) buf[7] = byte(v >> 56 & 0x0ff) boundParameters = append(boundParameters, oci8bind{dty, unsafe.Pointer(cdata)}) case bool: // oracle dont have bool, handle as 0/1 dty = C.SQLT_INT clen = C.sb4(1) cdata = (*C.char)(C.malloc(10)) if v { *cdata = 1 } else { *cdata = 0 } boundParameters = append(boundParameters, oci8bind{dty, unsafe.Pointer(cdata)}) default: dty = C.SQLT_CHR d := fmt.Sprintf("%v", v) clen = C.sb4(len(d)) cdata = C.CString(d) boundParameters = append(boundParameters, oci8bind{dty, unsafe.Pointer(cdata)}) } if uv.Name != "" { name := ":" + uv.Name cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) if rv := C.OCIBindByName( (*C.OCIStmt)(s.s), s.bp, (*C.OCIError)(s.c.err), (*C.OraText)(unsafe.Pointer(cname)), C.sb4(len(name)), unsafe.Pointer(cdata), clen, dty, nil, nil, nil, 0, nil, C.OCI_DEFAULT); rv != C.OCI_SUCCESS { } } else { if rv := C.OCIBindByPos( (*C.OCIStmt)(s.s), s.bp, (*C.OCIError)(s.c.err), C.ub4(i+1), unsafe.Pointer(cdata), clen, dty, nil, nil, nil, 0, nil, C.OCI_DEFAULT); rv != C.OCI_SUCCESS { defer freeBoundParameters(boundParameters) return nil, ociGetError(rv, s.c.err) } } } return boundParameters, nil }