You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
go-library/vendor/github.com/godror/godror/rows.go

800 lines
24 KiB

// Copyright 2017, 2020 The Godror Authors
//
//
// SPDX-License-Identifier: UPL-1.0 OR Apache-2.0
package godror
/*
#include "dpiImpl.h"
//int dpiData_getRowidStringValue(dpiData *data, const char **value, uint32_t *valueLength) {
// return dpiRowid_getStringValue(data->value.asRowid, value, valueLength);
//}
//dpiRowid *dpiData_getRowid(dpiData *data) {
// return data->value.asRowid;
//}
*/
import "C"
import (
"bytes"
"database/sql/driver"
"fmt"
"io"
"math"
"reflect"
"runtime"
"strconv"
"strings"
"time"
"unsafe"
)
var _ = driver.Rows((*rows)(nil))
var _ = driver.RowsColumnTypeDatabaseTypeName((*rows)(nil))
var _ = driver.RowsColumnTypeLength((*rows)(nil))
var _ = driver.RowsColumnTypeNullable((*rows)(nil))
var _ = driver.RowsColumnTypePrecisionScale((*rows)(nil))
var _ = driver.RowsColumnTypeScanType((*rows)(nil))
var _ = driver.RowsNextResultSet((*rows)(nil))
type rows struct {
err error
nextRsErr error
done chan struct{}
*statement
origSt *statement
nextRs *C.dpiStmt
data [][]C.dpiData
columns []Column
vars []*C.dpiVar
bufferRowIndex C.uint32_t
fetched C.uint32_t
fromData bool
}
// Columns returns the names of the columns. The number of
// columns of the result is inferred from the length of the
// slice. If a particular column name isn't known, an empty
// string should be returned for that entry.
func (r *rows) Columns() []string {
names := make([]string, len(r.columns))
for i, col := range r.columns {
names[i] = col.Name
}
return names
}
// Close closes the rows iterator.
func (r *rows) Close() error {
if r == nil {
return nil
}
vars, st, nextRs, done := r.vars, r.statement, r.nextRs, r.done
r.columns, r.vars, r.data, r.statement, r.nextRs, r.done = nil, nil, nil, nil, nil, nil
if done != nil {
close(done)
}
fromData := r.fromData
r.fromData = false
for _, v := range vars[:cap(vars)] {
if v != nil {
C.dpiVar_release(v)
}
}
if nextRs != nil {
if logger := getLogger(); logger != nil {
logger.Log("msg", "rows Close", "nextRs", fmt.Sprintf("%p", nextRs))
}
C.dpiStmt_release(nextRs)
}
if st == nil {
return nil
}
if fromData || st.dpiStmt.refCount < 2 {
return st.Close()
}
C.dpiStmt_release(st.dpiStmt)
return nil
}
// ColumnTypeLength return the length of the column type if the column is a variable length type.
// If the column is not a variable length type ok should return false.
// If length is not limited other than system limits, it should return math.MaxInt64.
// The following are examples of returned values for various types:
//
// TEXT (math.MaxInt64, true)
// varchar(10) (10, true)
// nvarchar(10) (10, true)
// decimal (0, false)
// int (0, false)
// bytea(30) (30, true)
func (r *rows) ColumnTypeLength(index int) (length int64, ok bool) {
switch col := r.columns[index]; col.OracleType {
case C.DPI_ORACLE_TYPE_ROWID, C.DPI_NATIVE_TYPE_ROWID:
return int64(10), true
case C.DPI_ORACLE_TYPE_VARCHAR, C.DPI_ORACLE_TYPE_NVARCHAR,
C.DPI_ORACLE_TYPE_CHAR, C.DPI_ORACLE_TYPE_NCHAR,
C.DPI_ORACLE_TYPE_LONG_VARCHAR, C.DPI_ORACLE_TYPE_LONG_NVARCHAR,
C.DPI_NATIVE_TYPE_BYTES:
return int64(col.Size), true
case C.DPI_ORACLE_TYPE_CLOB, C.DPI_ORACLE_TYPE_NCLOB,
C.DPI_ORACLE_TYPE_BLOB,
C.DPI_ORACLE_TYPE_BFILE,
C.DPI_NATIVE_TYPE_LOB,
C.DPI_ORACLE_TYPE_JSON, C.DPI_ORACLE_TYPE_JSON_OBJECT, C.DPI_ORACLE_TYPE_JSON_ARRAY:
return math.MaxInt64, true
default:
return 0, false
}
}
// ColumnTypeDatabaseTypeName returns the database system type name without the length.
// Type names should be uppercase.
// Examples of returned types: "VARCHAR", "NVARCHAR", "VARCHAR2", "CHAR", "TEXT", "DECIMAL", "SMALLINT", "INT", "BIGINT", "BOOL", "[]BIGINT", "JSONB", "XML", "TIMESTAMP".
func (r *rows) ColumnTypeDatabaseTypeName(index int) string {
switch r.columns[index].OrigOracleType {
case C.DPI_ORACLE_TYPE_VARCHAR:
return "VARCHAR2"
case C.DPI_ORACLE_TYPE_NVARCHAR:
return "NVARCHAR2"
case C.DPI_ORACLE_TYPE_CHAR:
return "CHAR"
case C.DPI_ORACLE_TYPE_NCHAR:
return "NCHAR"
case C.DPI_ORACLE_TYPE_LONG_VARCHAR, C.DPI_ORACLE_TYPE_LONG_NVARCHAR:
return "LONG"
case C.DPI_NATIVE_TYPE_BYTES, C.DPI_ORACLE_TYPE_RAW:
return "RAW"
case C.DPI_ORACLE_TYPE_ROWID, C.DPI_NATIVE_TYPE_ROWID:
return "ROWID"
case C.DPI_ORACLE_TYPE_LONG_RAW:
return "LONG RAW"
case C.DPI_ORACLE_TYPE_NUMBER:
return "NUMBER"
case C.DPI_ORACLE_TYPE_NATIVE_FLOAT, C.DPI_NATIVE_TYPE_FLOAT:
return "FLOAT"
case C.DPI_ORACLE_TYPE_NATIVE_DOUBLE, C.DPI_NATIVE_TYPE_DOUBLE:
return "DOUBLE"
case C.DPI_ORACLE_TYPE_NATIVE_INT, C.DPI_NATIVE_TYPE_INT64:
return "BINARY_INTEGER"
case C.DPI_ORACLE_TYPE_NATIVE_UINT, C.DPI_NATIVE_TYPE_UINT64:
return "BINARY_INTEGER"
case C.DPI_ORACLE_TYPE_TIMESTAMP, C.DPI_NATIVE_TYPE_TIMESTAMP:
return "TIMESTAMP"
case C.DPI_ORACLE_TYPE_TIMESTAMP_TZ:
return "TIMESTAMP WITH TIME ZONE"
case C.DPI_ORACLE_TYPE_TIMESTAMP_LTZ:
return "TIMESTAMP WITH LOCAL TIME ZONE"
case C.DPI_ORACLE_TYPE_DATE:
return "DATE"
case C.DPI_ORACLE_TYPE_INTERVAL_DS, C.DPI_NATIVE_TYPE_INTERVAL_DS:
return "INTERVAL DAY TO SECOND"
case C.DPI_ORACLE_TYPE_INTERVAL_YM, C.DPI_NATIVE_TYPE_INTERVAL_YM:
return "INTERVAL YEAR TO MONTH"
case C.DPI_ORACLE_TYPE_CLOB:
return "CLOB"
case C.DPI_ORACLE_TYPE_NCLOB:
return "NCLOB"
case C.DPI_ORACLE_TYPE_BLOB:
return "BLOB"
case C.DPI_ORACLE_TYPE_BFILE:
return "BFILE"
case C.DPI_ORACLE_TYPE_STMT, C.DPI_NATIVE_TYPE_STMT:
return "SYS_REFCURSOR"
case C.DPI_ORACLE_TYPE_BOOLEAN, C.DPI_NATIVE_TYPE_BOOLEAN:
return "BOOLEAN"
case C.DPI_ORACLE_TYPE_OBJECT:
return "OBJECT"
case C.DPI_ORACLE_TYPE_JSON, C.DPI_ORACLE_TYPE_JSON_OBJECT, C.DPI_ORACLE_TYPE_JSON_ARRAY:
return "JSON"
default:
return fmt.Sprintf("OTHER[%d]", r.columns[index].OracleType)
}
}
// ColumnTypeNullable. The nullable value should be true if it is known the column may be null, or false if the column is known to be not nullable. If the column nullability is unknown, ok should be false.
func (r *rows) ColumnTypeNullable(index int) (nullable, ok bool) {
return r.columns[index].Nullable, true
}
// ColumnTypePrecisionScale returns the precision and scale for decimal types.
// If not applicable, ok should be false.
// The following are examples of returned values for various types:
//
// decimal(38, 4) (38, 4, true)
// int (0, 0, false)
// decimal (math.MaxInt64, math.MaxInt64, true)
func (r *rows) ColumnTypePrecisionScale(index int) (precision, scale int64, ok bool) {
switch col := r.columns[index]; col.OracleType {
case
//C.DPI_ORACLE_TYPE_NATIVE_FLOAT, C.DPI_NATIVE_TYPE_FLOAT,
//C.DPI_ORACLE_TYPE_NATIVE_DOUBLE, C.DPI_NATIVE_TYPE_DOUBLE,
//C.DPI_ORACLE_TYPE_NATIVE_INT, C.DPI_NATIVE_TYPE_INT64,
//C.DPI_ORACLE_TYPE_NATIVE_UINT, C.DPI_NATIVE_TYPE_UINT64,
C.DPI_ORACLE_TYPE_NUMBER:
return int64(col.Precision), int64(col.Scale), true
default:
return 0, 0, false
}
}
// ColumnTypeScanType returns the value type that can be used to scan types into.
// For example, the database column type "bigint" this should return "reflect.TypeOf(int64(0))".
func (r *rows) ColumnTypeScanType(index int) reflect.Type {
switch col := r.columns[index]; col.OracleType {
case C.DPI_NATIVE_TYPE_BYTES, C.DPI_ORACLE_TYPE_RAW,
C.DPI_ORACLE_TYPE_ROWID, C.DPI_NATIVE_TYPE_ROWID,
C.DPI_ORACLE_TYPE_LONG_RAW:
return reflect.TypeOf([]byte(nil))
case C.DPI_ORACLE_TYPE_NUMBER:
switch col.NativeType {
case C.DPI_NATIVE_TYPE_INT64:
return reflect.TypeOf(int64(0))
case C.DPI_NATIVE_TYPE_UINT64:
return reflect.TypeOf(uint64(0))
//case C.DPI_NATIVE_TYPE_FLOAT:
// return reflect.TypeOf(float32(0))
//case C.DPI_NATIVE_TYPE_DOUBLE:
// return reflect.TypeOf(float64(0))
default:
return reflect.TypeOf(Number(""))
}
case C.DPI_ORACLE_TYPE_NATIVE_FLOAT, C.DPI_NATIVE_TYPE_FLOAT:
return reflect.TypeOf(float32(0))
case C.DPI_ORACLE_TYPE_NATIVE_DOUBLE, C.DPI_NATIVE_TYPE_DOUBLE:
return reflect.TypeOf(float64(0))
case C.DPI_ORACLE_TYPE_NATIVE_INT, C.DPI_NATIVE_TYPE_INT64:
return reflect.TypeOf(int64(0))
case C.DPI_ORACLE_TYPE_NATIVE_UINT, C.DPI_NATIVE_TYPE_UINT64:
return reflect.TypeOf(uint64(0))
case C.DPI_ORACLE_TYPE_TIMESTAMP, C.DPI_NATIVE_TYPE_TIMESTAMP,
C.DPI_ORACLE_TYPE_TIMESTAMP_TZ, C.DPI_ORACLE_TYPE_TIMESTAMP_LTZ,
C.DPI_ORACLE_TYPE_DATE:
return reflect.TypeOf(NullTime{})
case C.DPI_ORACLE_TYPE_INTERVAL_DS, C.DPI_NATIVE_TYPE_INTERVAL_DS:
return reflect.TypeOf(time.Duration(0))
case C.DPI_ORACLE_TYPE_CLOB, C.DPI_ORACLE_TYPE_NCLOB:
return reflect.TypeOf("")
case C.DPI_ORACLE_TYPE_BLOB, C.DPI_ORACLE_TYPE_BFILE:
return reflect.TypeOf([]byte(nil))
case C.DPI_ORACLE_TYPE_STMT, C.DPI_NATIVE_TYPE_STMT:
return reflect.TypeOf(&statement{})
case C.DPI_ORACLE_TYPE_BOOLEAN, C.DPI_NATIVE_TYPE_BOOLEAN:
return reflect.TypeOf(false)
case C.DPI_ORACLE_TYPE_JSON:
return reflect.TypeOf(JSON{})
case C.DPI_ORACLE_TYPE_JSON_OBJECT:
return reflect.TypeOf(JSONObject{})
case C.DPI_ORACLE_TYPE_JSON_ARRAY:
return reflect.TypeOf(JSONArray{})
default:
return reflect.TypeOf("")
}
}
const debugRowsNext = false
// Next is called to populate the next row of data into
// the provided slice. The provided slice will be the same
// size as the Columns() are wide.
//
// Next should return io.EOF when there are no more rows.
//
// As with all Objects, you MUST call Close on the returned Object instances when they're not needed anymore!
func (r *rows) Next(dest []driver.Value) error {
if r.err != nil {
return r.err
}
if len(dest) != len(r.columns) {
return fmt.Errorf("column count mismatch: we have %d columns, but given %d destination", len(r.columns), len(dest))
}
logger := getLogger()
runtime.LockOSThread()
defer runtime.UnlockOSThread()
if r.fetched == 0 {
// Start the watchdog only once See issue #113 (https://github.com/godror/godror/issues/113)
if r.done == nil {
if ctx := r.statement.ctx; ctx != nil {
// nil can be present when Next is issued on cursor returned from DB
if r.err = ctx.Err(); r.err != nil {
return r.err
}
if _, hasDeadline := r.statement.ctx.Deadline(); hasDeadline {
r.done = make(chan struct{})
// handle deadline for dpiStmt_fetchRows. context reused from stmt
if err := r.statement.handleDeadline(ctx, r.done); err != nil {
return err
}
}
}
}
if r.done != nil {
defer func() {
if r.err != nil && r.done != nil {
close(r.done)
r.done = nil
}
}()
}
var moreRows C.int
var start time.Time
maxRows := C.uint32_t(r.statement.FetchArraySize())
r.statement.Lock()
if debugRowsNext {
fmt.Printf("fetching max=%d\n", maxRows)
start = time.Now()
}
err := r.statement.checkExecNoLOT(func() C.int {
return C.dpiStmt_fetchRows(r.dpiStmt, maxRows, &r.bufferRowIndex, &r.fetched, &moreRows)
})
failed := err != nil
if debugRowsNext {
fmt.Printf("failed=%t bri=%d fetched=%d more=%d data=%d cols=%d dur=%s\n", failed, r.bufferRowIndex, r.fetched, moreRows, len(r.data), len(r.columns), time.Since(start))
}
r.statement.Unlock()
if failed {
if logger != nil {
logger.Log("msg", "fetch", "error", err)
}
_ = r.Close()
if strings.Contains(err.Error(), "DPI-1039: statement was already closed") {
r.err = io.EOF
} else {
r.err = fmt.Errorf("Next: %w", err)
}
return r.err
}
if logger != nil {
logger.Log("msg", "fetched", "bri", r.bufferRowIndex, "fetched", r.fetched, "moreRows", moreRows, "len(data)", len(r.data), "cols", len(r.columns))
}
if r.fetched == 0 {
_ = r.Close()
r.err = io.EOF
return r.err
}
if r.data == nil {
r.data = make([][]C.dpiData, len(r.columns))
for i := range r.columns {
var n C.uint32_t
var data *C.dpiData
if err = r.statement.checkExecNoLOT(func() C.int {
return C.dpiVar_getReturnedData(r.vars[i], 0, &n, &data)
}); err != nil {
return fmt.Errorf("getReturnedData[%d]: %w", i, err)
}
r.data[i] = unsafe.Slice(data, n)
//fmt.Printf("data %d=%+v\n%+v\n", n, data, r.data[i][0])
}
}
}
//fmt.Printf("data=%#v\n", r.data)
nullDate := r.statement.NullDate()
mkNumber := func(s string) interface{} { return Number(s) }
if r.statement.NumberAsString() {
mkNumber = func(s string) interface{} { return s }
}
//fmt.Printf("bri=%d fetched=%d\n", r.bufferRowIndex, r.fetched)
//fmt.Printf("data=%#v\n", r.data[0][r.bufferRowIndex])
//fmt.Printf("VC=%d\n", C.DPI_ORACLE_TYPE_VARCHAR)
for i, col := range r.columns {
typ := col.OracleType
d := &r.data[i][r.bufferRowIndex]
isNull := d.isNull == 1
if false && logger != nil {
logger.Log("msg", "Next", "i", i, "row", r.bufferRowIndex, "typ", typ, "null", isNull) //, "data", fmt.Sprintf("%+v", d), "typ", typ)
}
switch typ {
case C.DPI_ORACLE_TYPE_VARCHAR, C.DPI_ORACLE_TYPE_NVARCHAR,
C.DPI_ORACLE_TYPE_CHAR, C.DPI_ORACLE_TYPE_NCHAR,
C.DPI_ORACLE_TYPE_LONG_VARCHAR, C.DPI_ORACLE_TYPE_LONG_NVARCHAR:
//fmt.Printf("CHAR\n")
if isNull {
dest[i] = ""
continue
}
//b := C.dpiData_getBytes(d)
b := (*C.dpiBytes)(unsafe.Pointer(&d.value))
if b.length == 0 {
dest[i] = ""
continue
}
dest[i] = C.GoStringN(b.ptr, C.int(b.length))
case C.DPI_ORACLE_TYPE_NUMBER:
if isNull {
dest[i] = nil
continue
}
switch col.NativeType {
case C.DPI_NATIVE_TYPE_INT64:
//dest[i] = int64(C.dpiData_getInt64(d))
dest[i] = *((*int64)(unsafe.Pointer(&d.value)))
case C.DPI_NATIVE_TYPE_UINT64:
//dest[i] = uint64(C.dpiData_getUint64(d))
dest[i] = *((*uint64)(unsafe.Pointer(&d.value)))
case C.DPI_NATIVE_TYPE_FLOAT:
//dest[i] = float32(C.dpiData_getFloat(d))
//dest[i] = printFloat(float64(C.dpiData_getFloat(d)))
dest[i] = printFloat(float64(*((*float32)(unsafe.Pointer(&d.value)))))
case C.DPI_NATIVE_TYPE_DOUBLE:
//dest[i] = float64(C.dpiData_getDouble(d))
//dest[i] = printFloat(float64(C.dpiData_getDouble(d)))
dest[i] = printFloat(*((*float64)(unsafe.Pointer(&d.value))))
case C.DPI_NATIVE_TYPE_BOOLEAN:
dest[i] = d.isNull != 0 && C.dpiData_getBool(d) != 0
default:
//b := C.dpiData_getBytes(d)
b := (*C.dpiBytes)(unsafe.Pointer(&d.value))
s := C.GoStringN(b.ptr, C.int(b.length))
dest[i] = mkNumber(s)
if false && logger != nil {
logger.Log("msg", "b", "i", i, "ptr", b.ptr, "length", b.length, "typ", col.NativeType, "int64", C.dpiData_getInt64(d), "dest", dest[i])
}
}
if false && logger != nil {
logger.Log("msg", "num", "t", col.NativeType, "i", i, "dest", fmt.Sprintf("%T %+v", dest[i], dest[i]))
}
case C.DPI_ORACLE_TYPE_ROWID, C.DPI_NATIVE_TYPE_ROWID:
if isNull {
dest[i] = nil
continue
}
// ROWID as returned by OCIRowidToChar
//cRowid := C.dpiData_getRowid(d)
cRowid := *((**C.dpiRowid)(unsafe.Pointer(&d.value)))
var cBuf *C.char
var cLen C.uint32_t
if err := r.statement.checkExecNoLOT(func() C.int {
return C.dpiRowid_getStringValue(cRowid, &cBuf, &cLen)
}); err != nil {
return err
}
dest[i] = C.GoStringN(cBuf, C.int(cLen))
case C.DPI_ORACLE_TYPE_RAW, C.DPI_ORACLE_TYPE_LONG_RAW:
if isNull {
dest[i] = nil
continue
}
//b := C.dpiData_getBytes(d)
b := (*C.dpiBytes)(unsafe.Pointer(&d.value))
if b.length == 0 {
dest[i] = []byte{}
continue
}
dest[i] = C.GoBytes(unsafe.Pointer(b.ptr), C.int(b.length))
case C.DPI_ORACLE_TYPE_NATIVE_FLOAT, C.DPI_NATIVE_TYPE_FLOAT:
if isNull {
dest[i] = nil
continue
}
//dest[i] = float32(C.dpiData_getFloat(d))
dest[i] = *((*float32)(unsafe.Pointer(&d.value)))
case C.DPI_ORACLE_TYPE_NATIVE_DOUBLE, C.DPI_NATIVE_TYPE_DOUBLE:
if isNull {
dest[i] = nil
continue
}
//dest[i] = float64(C.dpiData_getDouble(d))
dest[i] = *((*float64)(unsafe.Pointer(&d.value)))
case C.DPI_ORACLE_TYPE_NATIVE_INT, C.DPI_NATIVE_TYPE_INT64:
if isNull {
dest[i] = nil
continue
}
//dest[i] = int64(C.dpiData_getInt64(d))
dest[i] = *((*int64)(unsafe.Pointer(&d.value)))
case C.DPI_ORACLE_TYPE_NATIVE_UINT, C.DPI_NATIVE_TYPE_UINT64:
if isNull {
dest[i] = nil
continue
}
//dest[i] = uint64(C.dpiData_getUint64(d))
dest[i] = *((*uint64)(unsafe.Pointer(&d.value)))
case C.DPI_ORACLE_TYPE_TIMESTAMP,
C.DPI_ORACLE_TYPE_TIMESTAMP_TZ, C.DPI_ORACLE_TYPE_TIMESTAMP_LTZ,
C.DPI_NATIVE_TYPE_TIMESTAMP,
C.DPI_ORACLE_TYPE_DATE:
if isNull {
dest[i] = nullDate
continue
}
//ts := C.dpiData_getTimestamp(d)
ts := *((*C.dpiTimestamp)(unsafe.Pointer(&d.value)))
tz := r.conn.Timezone()
if col.OracleType == C.DPI_ORACLE_TYPE_TIMESTAMP_TZ || col.OracleType == C.DPI_ORACLE_TYPE_TIMESTAMP_LTZ {
tz = timeZoneFor(ts.tzHourOffset, ts.tzMinuteOffset,
nil, // just obey to what's included in the data
)
}
if tz == nil {
if logger != nil {
logger.Log("msg", "DATE", "i", i, "tz", tz, "params", r.conn.params)
}
}
dest[i] = time.Date(int(ts.year), time.Month(ts.month), int(ts.day), int(ts.hour), int(ts.minute), int(ts.second), int(ts.fsecond), tz)
case C.DPI_ORACLE_TYPE_INTERVAL_DS, C.DPI_NATIVE_TYPE_INTERVAL_DS:
if isNull {
dest[i] = nil
continue
}
var t time.Duration
dataGetIntervalDS(&t, d)
dest[i] = t
case C.DPI_ORACLE_TYPE_INTERVAL_YM, C.DPI_NATIVE_TYPE_INTERVAL_YM:
if isNull {
dest[i] = nil
continue
}
//ym := C.dpiData_getIntervalYM(d)
ym := *((*C.dpiIntervalYM)(unsafe.Pointer(&d.value)))
dest[i] = strconv.Itoa(int(ym.years)) + "-" + strconv.Itoa(int(ym.months))
case C.DPI_ORACLE_TYPE_CLOB, C.DPI_ORACLE_TYPE_NCLOB,
C.DPI_ORACLE_TYPE_BLOB,
C.DPI_ORACLE_TYPE_BFILE,
C.DPI_NATIVE_TYPE_LOB:
isClob := typ == C.DPI_ORACLE_TYPE_CLOB || typ == C.DPI_ORACLE_TYPE_NCLOB
if isNull {
if isClob && (r.ClobAsString() || !r.LobAsReader()) {
dest[i] = ""
} else {
dest[i] = nil
}
continue
}
rdr := &dpiLobReader{dpiLob: C.dpiData_getLOB(d), drv: r.drv, IsClob: isClob}
if isClob && (r.ClobAsString() || !r.LobAsReader()) {
sb := stringBuilders.Get()
_, err := io.Copy(sb, rdr)
C.dpiLob_close(rdr.dpiLob)
if err != nil {
stringBuilders.Put(sb)
return err
}
dest[i] = sb.String()
stringBuilders.Put(sb)
continue
}
dest[i] = &Lob{Reader: rdr, IsClob: rdr.IsClob}
case C.DPI_ORACLE_TYPE_STMT, C.DPI_NATIVE_TYPE_STMT:
if isNull {
dest[i] = nil
continue
}
st := &statement{conn: r.conn, dpiStmt: C.dpiData_getStmt(d),
stmtOptions: r.statement.stmtOptions, // inherit parent statement's options
}
var colCount C.uint32_t
if err := r.statement.checkExecNoLOT(func() C.int {
return C.dpiStmt_getNumQueryColumns(st.dpiStmt, &colCount)
}); err != nil {
if logger != nil {
logger.Log("msg", "Next.getNumQueryColumns", "st", fmt.Sprintf("%p", st.dpiStmt), "error", err)
}
//C.dpiStmt_release(st.dpiStmt)
return fmt.Errorf("getNumQueryColumns: %w", err)
}
st.Lock()
r2, err := st.openRows(int(colCount))
st.Unlock()
if err != nil {
if logger != nil {
logger.Log("msg", "Next.openRows", "st", fmt.Sprintf("%p", st.dpiStmt), "error", err)
}
st.Close()
return err
}
r2.fromData = true
stmtSetFinalizer(st, "Next")
dest[i] = r2
case C.DPI_ORACLE_TYPE_BOOLEAN, C.DPI_NATIVE_TYPE_BOOLEAN:
if isNull {
dest[i] = nil
continue
}
//dest[i] = C.dpiData_getBool(d) == 1
dest[i] = *((*C.int)(unsafe.Pointer(&d.value))) == 1
case C.DPI_ORACLE_TYPE_OBJECT: //Default type used for named type columns in the database. Data is transferred to/from Oracle in Oracle's internal format.
if isNull {
dest[i] = nil
continue
}
o, err := wrapObject(r.conn, col.ObjectType, C.dpiData_getObject(d))
if err != nil {
return err
}
dest[i] = o
case C.DPI_ORACLE_TYPE_JSON, C.DPI_NATIVE_TYPE_JSON:
if isNull {
dest[i] = nil
continue
}
switch col.NativeType {
case C.DPI_NATIVE_TYPE_JSON:
dj := *((**C.dpiJson)(unsafe.Pointer(&(d.value))))
dest[i] = JSON{dpiJson: dj}
default:
}
case C.DPI_NATIVE_TYPE_JSON_OBJECT:
if isNull {
dest[i] = nil
continue
}
dest[i] = JSONObject{dpiJsonObject: ((*C.dpiJsonObject)(unsafe.Pointer(&d.value)))}
case C.DPI_NATIVE_TYPE_JSON_ARRAY:
if isNull {
dest[i] = nil
continue
}
dest[i] = JSONArray{dpiJsonArray: ((*C.dpiJsonArray)(unsafe.Pointer(&d.value)))}
default:
return fmt.Errorf("unsupported column type %d", typ)
}
//fmt.Printf("dest[%d]=%#v\n", i, dest[i])
}
r.bufferRowIndex++
r.fetched--
if debugRowsNext && r.fetched < 2 {
fmt.Printf("bri=%d fetched=%d\n", r.bufferRowIndex, r.fetched)
}
if logger != nil {
logger.Log("msg", "scanned", "row", r.bufferRowIndex, "dest", dest)
}
return nil
}
var _ = driver.Rows((*directRow)(nil))
type directRow struct {
conn *conn
query string
result []interface{}
args []string
}
func (dr *directRow) Columns() []string {
logger := getLogger()
if logger != nil {
logger.Log("directRow", "Columns")
}
switch dr.query {
case getConnection:
return []string{"CONNECTION"}
}
return nil
}
// Close closes the rows iterator.
func (dr *directRow) Close() error {
dr.conn = nil
dr.query = ""
dr.args = nil
dr.result = nil
return nil
}
// Next is called to populate the next row of data into
// the provided slice. The provided slice will be the same
// size as the Columns() are wide.
//
// Next should return io.EOF when there are no more rows.
func (dr *directRow) Next(dest []driver.Value) error {
logger := getLogger()
if logger != nil {
logger.Log("directRow", "Next", "query", dr.query, "dest", dest)
}
switch dr.query {
case getConnection:
*(dest[0].(*interface{})) = dr.result[0]
}
return nil
}
func (r *rows) getImplicitResult() {
if r == nil || r.nextRsErr != nil {
return
}
// use the original statement for the NextResultSet call.
st := r.origSt
if st == nil {
st = r.statement
r.origSt = st
}
if err := r.checkExec(func() C.int { return C.dpiStmt_getImplicitResult(st.dpiStmt, &r.nextRs) }); err != nil {
r.nextRsErr = fmt.Errorf("getImplicitResult: %w", err)
}
C.dpiStmt_addRef(r.nextRs)
}
func (r *rows) HasNextResultSet() bool {
if r == nil || r.statement == nil || r.conn == nil {
return false
}
if r.nextRs != nil {
return true
}
if cv := r.conn.drv.clientVersion; !(cv.Version > 12 || cv.Version == 12 && cv.Release >= 1) {
return false
}
if sv, err := r.conn.ServerVersion(); !(err == nil &&
(sv.Version > 12 || sv.Version == 12 && sv.Release >= 1)) {
return false
}
r.getImplicitResult()
return r.nextRs != nil
}
func (r *rows) NextResultSet() error {
if !r.HasNextResultSet() {
if r.nextRsErr != nil {
return r.nextRsErr
}
return fmt.Errorf("getImplicitResult: %w", io.EOF)
}
st := &statement{conn: r.conn, dpiStmt: r.nextRs}
var n C.uint32_t
logger := getLogger()
if err := r.checkExec(func() C.int { return C.dpiStmt_getNumQueryColumns(st.dpiStmt, &n) }); err != nil {
err = fmt.Errorf("getNumQueryColumns: %+v: %w", err, io.EOF)
if logger != nil {
logger.Log("msg", "NextResultSet.getNumQueryColumns", "st", fmt.Sprintf("%p", st.dpiStmt), "error", err)
}
//C.dpiStmt_release(st.dpiStmt)
return err
}
// keep the originam statement for the succeeding NextResultSet calls.
st.Lock()
nr, err := st.openRows(int(n))
st.Unlock()
if err != nil {
if logger != nil {
logger.Log("msg", "NextResultSet.openRows", "st", fmt.Sprintf("%p", st.dpiStmt), "error", err)
}
st.Close()
return err
}
stmtSetFinalizer(st, "NextResultSet")
nr.origSt = r.origSt
if nr.origSt == nil {
nr.origSt = r.statement
}
*r = *nr
return nil
}
func printFloat(f float64) string {
var a [40]byte
b := strconv.AppendFloat(a[:0], f, 'f', -1, 64)
i := bytes.IndexByte(b, '.')
if i < 0 {
return string(b)
}
for j := i + 1; j < len(b); j++ {
if b[j] != '0' {
return string(b)
}
}
return string(b[:i])
}