func (rc *OCI8Rows) Next(dest []driver.Value) error { rv := C.OCIStmtFetch( (*C.OCIStmt)(rc.s.s), (*C.OCIError)(rc.s.c.err), 1, C.OCI_FETCH_NEXT, C.OCI_DEFAULT) if rv == C.OCI_NO_DATA { return io.EOF } else if rv != C.OCI_SUCCESS { return ociGetError(rc.s.c.err) } for i := range dest { // TODO: switch rc.cols[i].ind if rc.cols[i].ind == -1 { // Null dest[i] = nil continue } else if rc.cols[i].ind != 0 { return errors.New(fmt.Sprintf("Unknown column indicator: %d, col %s", rc.cols[i].ind, rc.cols[i].name)) } switch rc.cols[i].kind { case C.SQLT_DAT: // for test, date are return as timestamp buf := (*[1 << 30]byte)(unsafe.Pointer(rc.cols[i].pbuf))[0:rc.cols[i].rlen] // TODO: Handle BCE dates (http://docs.oracle.com/cd/B12037_01/appdev.101/b10779/oci03typ.htm#438305) // TODO: Handle timezones (http://docs.oracle.com/cd/B12037_01/appdev.101/b10779/oci03typ.htm#443601) dest[i] = time.Date( (int(buf[0])-100)*100+(int(buf[1])-100), time.Month(int(buf[2])), int(buf[3]), int(buf[4])-1, int(buf[5])-1, int(buf[6])-1, 0, rc.s.c.location) case C.SQLT_BLOB, C.SQLT_CLOB: ptmp := unsafe.Pointer(uintptr(rc.cols[i].pbuf) + unsafe.Sizeof(unsafe.Pointer(nil))) bamt := (*C.ub4)(ptmp) *bamt = 0 ptmp = unsafe.Pointer(uintptr(rc.cols[i].pbuf) + unsafe.Sizeof(C.ub4(0)) + unsafe.Sizeof(unsafe.Pointer(nil))) b := (*[1 << 30]byte)(ptmp)[0:blobBufSize] var buf []byte again: rv = C.OCILobRead( (*C.OCISvcCtx)(rc.s.c.svc), (*C.OCIError)(rc.s.c.err), *(**C.OCILobLocator)(rc.cols[i].pbuf), bamt, 1, ptmp, C.ub4(blobBufSize), nil, nil, 0, C.SQLCS_IMPLICIT) if rv == C.OCI_NEED_DATA { buf = append(buf, b[:int(*bamt)]...) goto again } if rv != C.OCI_SUCCESS { return ociGetError(rc.s.c.err) } if rc.cols[i].kind == C.SQLT_BLOB { dest[i] = append(buf, b[:int(*bamt)]...) } else { dest[i] = string(append(buf, b[:int(*bamt)]...)) } case C.SQLT_CHR, C.SQLT_AFC, C.SQLT_AVC: buf := (*[1 << 30]byte)(unsafe.Pointer(rc.cols[i].pbuf))[0:rc.cols[i].rlen] switch { case rc.cols[i].ind == 0: // Normal dest[i] = string(buf) case rc.cols[i].ind == -2 || // Field longer than type (truncated) rc.cols[i].ind > 0: // Field longer than type (truncated). Value is original length. dest[i] = string(buf) default: return errors.New(fmt.Sprintf("Unknown column indicator: %d", rc.cols[i].ind)) } case C.SQLT_BIN: // RAW buf := (*[1 << 30]byte)(unsafe.Pointer(rc.cols[i].pbuf))[0:rc.cols[i].rlen] dest[i] = buf case C.SQLT_NUM: // NUMBER buf := (*[21]byte)(unsafe.Pointer(rc.cols[i].pbuf)) dest[i] = buf case C.SQLT_VNU: // VARNUM buf := (*[22]byte)(unsafe.Pointer(rc.cols[i].pbuf)) dest[i] = buf case C.SQLT_INT: // INT buf := (*[1 << 30]byte)(unsafe.Pointer(rc.cols[i].pbuf))[0:rc.cols[i].rlen] dest[i] = buf case C.SQLT_LNG: // LONG buf := (*[1 << 30]byte)(unsafe.Pointer(rc.cols[i].pbuf))[0:rc.cols[i].rlen] dest[i] = buf case C.SQLT_IBDOUBLE, C.SQLT_IBFLOAT: colsize := rc.cols[i].size buf := (*[1 << 30]byte)(unsafe.Pointer(rc.cols[i].pbuf))[0:colsize] if colsize == 4 { v := uint32(buf[3]) v |= uint32(buf[2]) << 8 v |= uint32(buf[1]) << 16 v |= uint32(buf[0]) << 24 // Don't know why bits are inverted that way, but it works if buf[0]&0x80 == 0 { v ^= 0xffffffff } else { v &= 0x7fffffff } dest[i] = math.Float32frombits(v) } else if colsize == 8 { v := uint64(buf[7]) v |= uint64(buf[6]) << 8 v |= uint64(buf[5]) << 16 v |= uint64(buf[4]) << 24 v |= uint64(buf[3]) << 32 v |= uint64(buf[2]) << 40 v |= uint64(buf[1]) << 48 v |= uint64(buf[0]) << 56 // Don't know why bits are inverted that way, but it works if buf[0]&0x80 == 0 { v ^= 0xffffffffffffffff } else { v &= 0x7fffffffffffffff } dest[i] = math.Float64frombits(v) } else { return errors.New(fmt.Sprintf("Unhandled binary float size: %d", colsize)) } case C.SQLT_TIMESTAMP: if rv := C.WrapOCIDateTimeGetDateTime( (*C.OCIEnv)(rc.s.c.env), (*C.OCIError)(rc.s.c.err), *(**C.OCIDateTime)(rc.cols[i].pbuf), ); rv.rv != C.OCI_SUCCESS { return ociGetError(rc.s.c.err) } else { dest[i] = time.Date( int(rv.y), time.Month(rv.m), int(rv.d), int(rv.hh), int(rv.mm), int(rv.ss), int(rv.ff), rc.s.c.location) } case C.SQLT_TIMESTAMP_TZ, C.SQLT_TIMESTAMP_LTZ: tptr := *(**C.OCIDateTime)(rc.cols[i].pbuf) rv := C.WrapOCIDateTimeGetDateTime( (*C.OCIEnv)(rc.s.c.env), (*C.OCIError)(rc.s.c.err), tptr) if rv.rv != C.OCI_SUCCESS { return ociGetError(rc.s.c.err) } rvz := C.WrapOCIDateTimeGetTimeZoneNameOffset( (*C.OCIEnv)(rc.s.c.env), (*C.OCIError)(rc.s.c.err), tptr) if rvz.rv != C.OCI_SUCCESS { return ociGetError(rc.s.c.err) } nnn := C.GoStringN((*C.char)((unsafe.Pointer)(&rvz.zone[0])), C.int(rvz.zlen)) loc, err := time.LoadLocation(nnn) if err != nil { // TODO: reuse locations loc = time.FixedZone(nnn, int(rvz.h)*60*60+int(rvz.m)*60) } dest[i] = time.Date( int(rv.y), time.Month(rv.m), int(rv.d), int(rv.hh), int(rv.mm), int(rv.ss), int(rv.ff), loc) case C.SQLT_INTERVAL_DS: iptr := *(**C.OCIInterval)(rc.cols[i].pbuf) rv := C.WrapOCIIntervalGetDaySecond( (*C.OCIEnv)(rc.s.c.env), (*C.OCIError)(rc.s.c.err), iptr) if rv.rv != C.OCI_SUCCESS { return ociGetError(rc.s.c.err) } dest[i] = int64(time.Duration(rv.d)*time.Hour*24 + time.Duration(rv.hh)*time.Hour + time.Duration(rv.mm)*time.Minute + time.Duration(rv.ss)*time.Second + time.Duration(rv.ff)) case C.SQLT_INTERVAL_YM: iptr := *(**C.OCIInterval)(rc.cols[i].pbuf) rv := C.WrapOCIIntervalGetYearMonth( (*C.OCIEnv)(rc.s.c.env), (*C.OCIError)(rc.s.c.err), iptr) if rv.rv != C.OCI_SUCCESS { return ociGetError(rc.s.c.err) } dest[i] = int64(rv.y)*12 + int64(rv.m) default: return errors.New(fmt.Sprintf("Unhandled column type: %d", rc.cols[i].kind)) } } return 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 }