1 Star 1 Fork 0

tdus / godror

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
stmt.go 56.53 KB
一键复制 编辑 原始数据 按行查看 历史
Tamás Gulácsi 提交于 2020-04-25 07:42 . Make shadow happy
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312
// Copyright 2017, 2020 The Godror Authors
//
//
// SPDX-License-Identifier: UPL-1.0 OR Apache-2.0
package godror
/*
#include <stdlib.h>
#include "dpiImpl.h"
const int sizeof_dpiData = sizeof(void);
void godror_setFromString(dpiVar *dv, uint32_t pos, const _GoString_ value) {
uint32_t length;
length = _GoStringLen(value);
if( length == 0 ) {
return;
}
dpiVar_setFromBytes(dv, pos, _GoStringPtr(value), length);
}
*/
import "C"
import (
"context"
"database/sql"
"database/sql/driver"
"fmt"
"io"
"reflect"
"strconv"
"strings"
"sync"
"time"
"unsafe"
errors "golang.org/x/xerrors"
)
type stmtOptions struct {
boolString boolString
fetchRowCount int
arraySize int
callTimeout time.Duration
execMode C.dpiExecMode
plSQLArrays bool
lobAsReader bool
}
type boolString struct {
True, False string
}
func (bs boolString) IsZero() bool { return bs.True == "" && bs.False == "" }
func (bs boolString) MaxLen() int {
n := len(bs.True)
if m := len(bs.False); m > n {
return m
}
return n
}
func (bs boolString) ToString(b bool) string {
if b {
return bs.True
}
return bs.False
}
func (bs boolString) FromString(s string) bool {
return s == bs.True && bs.True != "" || bs.True == "" && s != bs.False
}
func (o stmtOptions) BoolString() boolString { return o.boolString }
func (o stmtOptions) ExecMode() C.dpiExecMode {
if o.execMode == 0 {
return C.DPI_MODE_EXEC_DEFAULT
}
return o.execMode
}
func (o stmtOptions) ArraySize() int {
if o.arraySize <= 0 {
return DefaultArraySize
} else if o.arraySize > 1<<16 {
return 1 << 16
}
return o.arraySize
}
func (o stmtOptions) FetchRowCount() int {
if o.fetchRowCount <= 0 {
return DefaultFetchRowCount
}
return o.fetchRowCount
}
func (o stmtOptions) PlSQLArrays() bool { return o.plSQLArrays }
func (o stmtOptions) ClobAsString() bool { return !o.lobAsReader }
func (o stmtOptions) LobAsReader() bool { return o.lobAsReader }
// Option holds statement options.
type Option func(*stmtOptions)
// BoolToString is an option that governs convertsion from bool to string in the database.
// This is for converting from bool to string, from outside of the database
// (which does not have a BOOL(EAN) column (SQL) type, only a BOOLEAN PL/SQL type).
//
// This will be used only with DML statements and when the PlSQLArrays Option is not used.
//
// For the other way around, use an sql.Scanner that converts from string to bool. For example:
//
// type Booler bool
// var _ sql.Scanner = Booler{}
// func (b Booler) Scan(src interface{}) error {
// switch src := src.(type) {
// case int: *b = x == 1
// case string: *b = x == "Y" || x == "T" // or any string your database model treats as truth value
// default: return fmt.Errorf("unknown scanner source %T", src)
// }
// return nil
// }
//
// Such a type cannot be included in this package till we can inject the truth strings into the scanner method.
func BoolToString(trueVal, falseVal string) Option {
return func(o *stmtOptions) { o.boolString = boolString{True: trueVal, False: falseVal} }
}
// PlSQLArrays is to signal that the slices given in arguments of Exec to
// be left as is - the default is to treat them as arguments for ExecMany.
var PlSQLArrays Option = func(o *stmtOptions) { o.plSQLArrays = true }
// FetchRowCount returns an option to set the rows to be fetched, overriding DefaultFetchRowCount.
func FetchRowCount(rowCount int) Option {
if rowCount <= 0 {
return nil
}
return func(o *stmtOptions) { o.fetchRowCount = rowCount }
}
// ArraySize returns an option to set the array size to be used, overriding DefaultArraySize.
func ArraySize(arraySize int) Option {
if arraySize <= 0 {
return nil
}
return func(o *stmtOptions) { o.arraySize = arraySize }
}
func parseOnly(o *stmtOptions) { o.execMode = C.DPI_MODE_EXEC_PARSE_ONLY }
// ParseOnly returns an option to set the ExecMode to only Parse.
func ParseOnly() Option {
return parseOnly
}
func describeOnly(o *stmtOptions) { o.execMode = C.DPI_MODE_EXEC_DESCRIBE_ONLY }
// ClobAsString returns an option to force fetching CLOB columns as strings.
//
// Deprecated: CLOBs are returned as string by default - for CLOB, use LobAsReader.
func ClobAsString() Option { return func(o *stmtOptions) { o.lobAsReader = false } }
// LobAsReader is an option to set query columns of CLOB/BLOB to be returned as a Lob.
//
// LOB as a reader and writer is not the most performant at all. Yes, OCI
// and ODPI-C provide a way to retrieve this data directly. Effectively,
// all you need to do is tell ODPI-C that you want a "long string" or "long
// raw" returned. You can do that by telling ODPI-C you want a variable
// with oracleTypeNum=DPI_ORACLE_TYPE_LONG_VARCHAR or
// DPI_ORACLE_TYPE_LONG_RAW and nativeTypeNum=DPI_NATIVE_TYPE_BYTES. ODPI-C
// will handle all of the dynamic fetching and allocation that is required.
// :-) You can also use DPI_ORACLE_TYPE_VARCHAR and DPI_ORACLE_TYPE_RAW as
// long as you set the size > 32767 -- whichever way you wish to use.
//
// With the use of LOBs, there is one round-trip to get the LOB locators,
// then a round-trip for each read() that is performed. If you request the
// length there is another round-trip required. So if you fetch 100 rows
// with 2 CLOB columns, that means you get 401 round-trips. Using
// string/[]bytes directly means only one round trip. So you can see that
// if your database is remote with high latency you can have a significant
// performance penalty!
func LobAsReader() Option { return func(o *stmtOptions) { o.lobAsReader = true } }
// CallTimeout sets the round-trip timeout (OCI_ATTR_CALL_TIMEOUT).
//
// See https://docs.oracle.com/en/database/oracle/oracle-database/18/lnoci/handle-and-descriptor-attributes.html#GUID-D8EE68EB-7E38-4068-B06E-DF5686379E5E
func CallTimeout(d time.Duration) Option {
return func(o *stmtOptions) { o.callTimeout = d }
}
const minChunkSize = 1 << 16
var _ = driver.Stmt((*statement)(nil))
var _ = driver.StmtQueryContext((*statement)(nil))
var _ = driver.StmtExecContext((*statement)(nil))
var _ = driver.NamedValueChecker((*statement)(nil))
type statement struct {
stmtOptions
columns []Column
isSlice []bool
gets []dataGetter
dests []interface{}
data [][]C.dpiData
vars []*C.dpiVar
varInfos []varInfo
query string
sync.Mutex
arrLen int
*conn
dpiStmt *C.dpiStmt
dpiStmtInfo C.dpiStmtInfo
}
type dataGetter func(v interface{}, data []C.dpiData) error
// Close closes the statement.
//
// As of Go 1.1, a Stmt will not be closed if it's in use
// by any queries.
func (st *statement) Close() error {
if st == nil {
return nil
}
st.Lock()
defer st.Unlock()
return st.close(false)
}
func (st *statement) close(keepDpiStmt bool) error {
if st == nil {
return nil
}
c, dpiStmt, vars := st.conn, st.dpiStmt, st.vars
st.isSlice = nil
st.query = ""
st.data = nil
st.vars = nil
st.varInfos = nil
st.gets = nil
st.dests = nil
st.columns = nil
st.dpiStmt = nil
st.conn = nil
st.dpiStmtInfo = C.dpiStmtInfo{}
for _, v := range vars[:cap(vars)] {
if v != nil {
C.dpiVar_release(v)
}
}
if !keepDpiStmt {
var si C.dpiStmtInfo
if dpiStmt != nil &&
C.dpiStmt_getInfo(dpiStmt, &si) != C.DPI_FAILURE && // this is just to check the validity of dpiStmt, to avoid SIGSEGV
C.dpiStmt_release(dpiStmt) != C.DPI_FAILURE {
return nil
}
}
if c == nil {
return driver.ErrBadConn
}
if err := c.getError(); err != nil {
return errors.Errorf("statement/dpiStmt_release: %w", err)
}
return nil
}
// Exec executes a query that doesn't return rows, such
// as an INSERT or UPDATE.
//
// Deprecated: Drivers should implement StmtExecContext instead (or additionally).
func (st *statement) Exec(args []driver.Value) (driver.Result, error) {
nargs := make([]driver.NamedValue, len(args))
for i, arg := range args {
nargs[i].Ordinal = i + 1
nargs[i].Value = arg
}
return st.ExecContext(context.Background(), nargs)
}
// Query executes a query that may return rows, such as a
// SELECT.
//
// Deprecated: Drivers should implement StmtQueryContext instead (or additionally).
func (st *statement) Query(args []driver.Value) (driver.Rows, error) {
nargs := make([]driver.NamedValue, len(args))
for i, arg := range args {
nargs[i].Ordinal = i + 1
nargs[i].Value = arg
}
return st.QueryContext(context.Background(), nargs)
}
// ExecContext executes a query that doesn't return rows, such as an INSERT or UPDATE.
//
// ExecContext must honor the context timeout and return when it is canceled.
//
// Cancelation/timeout is honored, execution is broken, but you may have to disable out-of-bound execution - see https://github.com/oracle/odpi/issues/116 for details.
func (st *statement) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) {
if err := ctx.Err(); err != nil {
return nil, err
}
Log := ctxGetLog(ctx)
st.Lock()
defer st.Unlock()
if st.conn == nil {
return nil, driver.ErrBadConn
}
if st.dpiStmt == nil && st.query == getConnection {
*(args[0].Value.(sql.Out).Dest.(*interface{})) = st.conn
return driver.ResultNoRows, nil
}
st.conn.mu.RLock()
defer st.conn.mu.RUnlock()
closeIfBadConn := func(err error) error {
if err != nil && err == driver.ErrBadConn {
if Log != nil {
Log("error", err)
}
st.close(false)
st.conn.close(true)
}
return err
}
// bind variables
if err := st.bindVars(args, Log); err != nil {
return nil, closeIfBadConn(err)
}
mode := st.ExecMode()
//fmt.Printf("%p.%p: inTran? %t\n%s\n", st.conn, st, st.inTransaction, st.query)
if !st.inTransaction {
mode |= C.DPI_MODE_EXEC_COMMIT_ON_SUCCESS
}
st.setCallTimeout(ctx)
done := make(chan error, 1)
// execute
go func() {
defer close(done)
var err error
Loop:
for i := 0; i < 3; i++ {
if err = ctx.Err(); err != nil {
done <- err
return
}
if !st.PlSQLArrays() && st.arrLen > 0 {
if Log != nil {
Log("C", "dpiStmt_executeMany", "mode", mode, "len", st.arrLen)
}
if C.dpiStmt_executeMany(st.dpiStmt, mode, C.uint32_t(st.arrLen)) == C.DPI_FAILURE {
if err = ctx.Err(); err == nil {
err = st.getError()
}
}
} else {
var colCount C.uint32_t
if Log != nil {
Log("C", "dpiStmt_execute", "mode", mode, "colCount", colCount)
}
if C.dpiStmt_execute(st.dpiStmt, mode, &colCount) == C.DPI_FAILURE {
if err = ctx.Err(); err == nil {
err = st.getError()
}
}
}
if Log != nil {
Log("msg", "st.Execute", "error", err)
}
if err == nil {
break
}
var cdr interface{ Code() int }
if !errors.As(err, &cdr) {
break
}
switch code := cdr.Code(); code {
// ORA-04068: "existing state of packages has been discarded"
case 4061, 4065, 4068:
if Log != nil {
Log("msg", "retry", "ora", code)
}
continue Loop
}
break
}
if err == nil {
done <- nil
return
}
done <- maybeBadConn(errors.Errorf("dpiStmt_execute(mode=%d arrLen=%d): %w", mode, st.arrLen, err), nil)
}()
select {
case err := <-done:
if err != nil {
return nil, closeIfBadConn(err)
}
case <-ctx.Done():
// select again to avoid race condition if both are done
select {
case err := <-done:
if err != nil {
return nil, closeIfBadConn(err)
}
case <-ctx.Done():
if Log != nil {
Log("msg", "BREAK statement")
}
_ = st.Break()
// For some reasons this SIGSEGVs if not not keepDpiStmt (try to close it),
st.close(true)
// so we hope that the following conn.Close closes the dpiStmt, too.
if err := st.conn.Close(); err != nil {
return nil, err
}
return nil, ctx.Err()
}
}
if Log != nil {
Log("gets", st.gets, "dests", st.dests)
}
for i, get := range st.gets {
if get == nil {
continue
}
if st.dpiStmtInfo.isReturning == 1 {
var n C.uint32_t
data := &st.data[i][0]
if C.dpiVar_getReturnedData(st.vars[i], 0, &n, &data) == C.DPI_FAILURE {
err := st.getError()
return nil, errors.Errorf("%d.getReturnedData: %w", i, closeIfBadConn(err))
}
if n == 0 {
st.data[i] = st.data[i][:0]
} else {
st.data[i] = (*(*[maxArraySize]C.dpiData)(unsafe.Pointer(data)))[:int(n):int(n)]
}
}
dest := st.dests[i]
if !st.isSlice[i] {
if err := get(dest, st.data[i]); err != nil {
if Log != nil {
Log("get", i, "error", err)
}
return nil, errors.Errorf("%d. get[%d]: %w", i, 0, closeIfBadConn(err))
}
continue
}
var n C.uint32_t = 1
if C.dpiVar_getNumElementsInArray(st.vars[i], &n) == C.DPI_FAILURE {
err := st.getError()
if Log != nil {
Log("msg", "getNumElementsInArray", "i", i, "error", err)
}
return nil, errors.Errorf("%d.getNumElementsInArray: %w", i, closeIfBadConn(err))
}
//fmt.Printf("i=%d dest=%T %#v\n", i, dest, dest)
if err := get(dest, st.data[i][:n]); err != nil {
if Log != nil {
Log("msg", "get", "i", i, "n", n, "error", err)
}
return nil, errors.Errorf("%d. get: %w", i, closeIfBadConn(err))
}
}
var count C.uint64_t
if C.dpiStmt_getRowCount(st.dpiStmt, &count) == C.DPI_FAILURE {
return nil, nil
}
return driver.RowsAffected(count), nil
}
// QueryContext executes a query that may return rows, such as a SELECT.
//
// QueryContext must honor the context timeout and return when it is canceled.
//
// Cancelation/timeout is honored, execution is broken, but you may have to disable out-of-bound execution - see https://github.com/oracle/odpi/issues/116 for details.
func (st *statement) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) {
if err := ctx.Err(); err != nil {
return nil, err
}
Log := ctxGetLog(ctx)
st.Lock()
defer st.Unlock()
if st.conn == nil {
return nil, driver.ErrBadConn
}
st.conn.mu.RLock()
defer st.conn.mu.RUnlock()
switch st.query {
case getConnection:
if Log != nil {
Log("msg", "QueryContext", "args", args)
}
return &directRow{conn: st.conn, query: st.query, result: []interface{}{st.conn}}, nil
case wrapResultset:
if Log != nil {
Log("msg", "QueryContext", "args", args)
}
return args[0].Value.(driver.Rows), nil
}
closeIfBadConn := func(err error) error {
if err != nil && err == driver.ErrBadConn {
if Log != nil {
Log("error", err)
}
st.close(false)
st.conn.close(true)
}
return err
}
//fmt.Printf("QueryContext(%+v)\n", args)
// bind variables
if err := st.bindVars(args, Log); err != nil {
return nil, closeIfBadConn(err)
}
mode := st.ExecMode()
//fmt.Printf("%p.%p: inTran? %t\n%s\n", st.conn, st, st.inTransaction, st.query)
if !st.inTransaction {
mode |= C.DPI_MODE_EXEC_COMMIT_ON_SUCCESS
}
// execute
var colCount C.uint32_t
done := make(chan error, 1)
go func() {
var err error
defer close(done)
for i := 0; i < 3; i++ {
if err = ctx.Err(); err != nil {
done <- err
return
}
st.setCallTimeout(ctx)
if C.dpiStmt_execute(st.dpiStmt, mode, &colCount) != C.DPI_FAILURE {
break
}
if err = ctx.Err(); err == nil {
err = st.getError()
if c, ok := err.(interface{ Code() int }); ok && c.Code() != 4068 {
break
}
}
}
if err == nil {
done <- nil
return
}
done <- maybeBadConn(errors.Errorf("dpiStmt_execute: %w", err), nil)
}()
select {
case err := <-done:
if err != nil {
return nil, closeIfBadConn(err)
}
case <-ctx.Done():
select {
case err := <-done:
if err != nil {
return nil, closeIfBadConn(err)
}
case <-ctx.Done():
if Log != nil {
Log("msg", "BREAK query")
}
_ = st.Break()
// For some reasons this SIGSEGVs if not not keepDpiStmt (try to close it),
st.close(true)
// so we hope that the following conn.Close closes the dpiStmt, too.
if err := st.conn.Close(); err != nil {
return nil, err
}
return nil, ctx.Err()
}
}
rows, err := st.openRows(int(colCount))
return rows, closeIfBadConn(err)
}
// NumInput returns the number of placeholder parameters.
//
// If NumInput returns >= 0, the sql package will sanity check
// argument counts from callers and return errors to the caller
// before the statement's Exec or Query methods are called.
//
// NumInput may also return -1, if the driver doesn't know
// its number of placeholders. In that case, the sql package
// will not sanity check Exec or Query argument counts.
func (st *statement) NumInput() int {
if st.query == wrapResultset {
return 1
}
if st.dpiStmt == nil {
switch st.query {
case getConnection, wrapResultset:
return 1
}
return 0
}
st.Lock()
defer st.Unlock()
var cnt C.uint32_t
//defer func() { fmt.Printf("%p.NumInput=%d (%q)\n", st, cnt, st.query) }()
if C.dpiStmt_getBindCount(st.dpiStmt, &cnt) == C.DPI_FAILURE {
if st.conn == nil {
panic(driver.ErrBadConn)
}
panic(st.conn.getError())
}
if cnt < 2 { // 1 can't decrease...
return int(cnt)
}
names := make([]*C.char, int(cnt))
lengths := make([]C.uint32_t, int(cnt))
if C.dpiStmt_getBindNames(st.dpiStmt, &cnt, &names[0], &lengths[0]) == C.DPI_FAILURE {
if st.conn == nil {
panic(driver.ErrBadConn)
}
panic(st.conn.getError())
}
//fmt.Printf("%p.NumInput=%d\n", st, cnt)
// return the number of *unique* arguments
return int(cnt)
}
func (st *statement) setCallTimeout(ctx context.Context) {
if st.callTimeout != 0 {
var cancel context.CancelFunc
ctx, cancel = context.WithTimeout(ctx, st.callTimeout)
_ = cancel
}
st.conn.setCallTimeout(ctx)
}
type argInfo struct {
objType *C.dpiObjectType
set dataSetter
bufSize int
typ C.dpiOracleTypeNum
natTyp C.dpiNativeTypeNum
isIn, isOut bool
}
// bindVars binds the given args into new variables.
func (st *statement) bindVars(args []driver.NamedValue, Log logFunc) error {
if Log != nil {
Log("enter", "bindVars", "args", args)
}
for i, v := range st.vars[:cap(st.vars)] {
if v != nil {
C.dpiVar_release(v)
st.vars[i], st.varInfos[i] = nil, varInfo{}
}
}
var named bool
if cap(st.vars) < len(args) {
st.vars = make([]*C.dpiVar, len(args))
} else {
st.vars = st.vars[:len(args)]
}
if cap(st.varInfos) < len(args) {
st.varInfos = make([]varInfo, len(args))
} else {
st.varInfos = st.varInfos[:len(args)]
}
if cap(st.data) < len(args) {
st.data = make([][]C.dpiData, len(args))
} else {
st.data = st.data[:len(args)]
}
if cap(st.gets) < len(args) {
st.gets = make([]dataGetter, len(args))
} else {
st.gets = st.gets[:len(args)]
}
if cap(st.dests) < len(args) {
st.dests = make([]interface{}, len(args))
} else {
st.dests = st.dests[:len(args)]
}
if cap(st.isSlice) < len(args) {
st.isSlice = make([]bool, len(args))
} else {
st.isSlice = st.isSlice[:len(args)]
}
rArgs := make([]reflect.Value, len(args))
minArrLen, maxArrLen := -1, -1
st.arrLen = minArrLen
maxArraySize := st.ArraySize()
infos := make([]argInfo, len(args))
//fmt.Printf("bindVars %d\n", len(args))
for i, a := range args {
st.gets[i] = nil
st.dests[i] = nil
if !named {
named = a.Name != ""
}
info := &(infos[i])
info.isIn = true
value := a.Value
if out, ok := value.(sql.Out); ok {
if !st.PlSQLArrays() && st.arrLen > 1 {
st.arrLen = maxArraySize
}
info.isIn, info.isOut = out.In, true
value = out.Dest
}
st.dests[i] = value
rv := reflect.ValueOf(value)
if info.isOut {
if rv.IsNil() {
fmt.Printf("%d. v=%T %#v kind=%s\n", i, value, value, reflect.ValueOf(value).Kind())
}
if rv.Kind() == reflect.Ptr {
rv = rv.Elem()
value = rv.Interface()
}
}
st.isSlice[i] = false
rArgs[i] = rv
if rv.Kind() == reflect.Ptr {
// deref in rArgs, but NOT value!
rArgs[i] = rv.Elem()
}
if _, isByteSlice := value.([]byte); !isByteSlice {
st.isSlice[i] = rArgs[i].Kind() == reflect.Slice
if !st.PlSQLArrays() && st.isSlice[i] {
n := rArgs[i].Len()
if minArrLen == -1 || n < minArrLen {
minArrLen = n
}
if maxArrLen == -1 || n > maxArrLen {
maxArrLen = n
}
}
}
if Log != nil {
Log("msg", "bindVars", "i", i, "in", info.isIn, "out", info.isOut, "value", fmt.Sprintf("%T %#v", st.dests[i], st.dests[i]))
}
}
if maxArrLen > maxArraySize {
if st.arrLen == maxArraySize {
st.arrLen = maxArrLen
}
maxArraySize = maxArrLen
}
doManyCount := 1
doExecMany := !st.PlSQLArrays()
if doExecMany {
if minArrLen != -1 && minArrLen != maxArrLen {
return errors.Errorf("PlSQLArrays is not set, but has different lengthed slices (min=%d < %d=max)", minArrLen, maxArrLen)
}
st.arrLen = minArrLen
if doExecMany = st.arrLen > 1; doExecMany {
doManyCount = st.arrLen
}
}
if Log != nil {
Log("doManyCount", doManyCount, "arrLen", st.arrLen, "doExecMany", doExecMany, "minArrLen", "maxArrLen")
}
for i := range args {
info := &(infos[i])
value := st.dests[i]
var err error
if value, err = st.bindVarTypeSwitch(info, &(st.gets[i]), value); err != nil {
return errors.Errorf("%d. arg: %w", i+1, err)
}
var rv reflect.Value
if st.isSlice[i] {
rv = reflect.ValueOf(value)
}
n := doManyCount
if st.PlSQLArrays() && st.isSlice[i] {
n = rv.Len()
if info.isOut {
n = rv.Cap()
}
}
if Log != nil {
Log("msg", "newVar", "i", i, "plSQLArrays", st.PlSQLArrays(), "typ", int(info.typ), "natTyp", int(info.natTyp), "sliceLen", n, "bufSize", info.bufSize, "isSlice", st.isSlice[i])
}
//i, st.PlSQLArrays(), info.typ, info.natTyp dataSliceLen, info.bufSize)
vi := varInfo{
IsPLSArray: st.PlSQLArrays() && st.isSlice[i],
Typ: info.typ, NatTyp: info.natTyp,
SliceLen: n, BufSize: info.bufSize,
ObjectType: info.objType,
}
if vi.IsPLSArray && vi.SliceLen > maxArraySize {
return errors.Errorf("maximum array size allowed is %d", maxArraySize)
}
if st.vars[i] == nil || st.data[i] == nil || st.varInfos[i] != vi {
if st.vars[i], st.data[i], err = st.newVar(vi); err != nil {
return errors.Errorf("%d: %w", i, err)
}
st.varInfos[i] = vi
}
// Have to setNumElementsInArray for the actual lengths for PL/SQL arrays
dv, data := st.vars[i], st.data[i]
if !info.isIn {
if st.PlSQLArrays() {
if Log != nil {
Log("C", "dpiVar_setNumElementsInArray", "i", i, "n", 0)
}
if C.dpiVar_setNumElementsInArray(dv, C.uint32_t(0)) == C.DPI_FAILURE {
return errors.Errorf("setNumElementsInArray[%d](%d): %w", i, 0, st.getError())
}
}
continue
}
if !st.isSlice[i] {
if Log != nil {
Log("msg", "set", "i", i, "value", fmt.Sprintf("%T=%#v", value, value))
}
if err := info.set(dv, data[:1], value); err != nil {
return errors.Errorf("set(data[%d][%d], %#v (%T)): %w", i, 0, value, value, err)
}
continue
}
if st.PlSQLArrays() {
n = rv.Len()
if Log != nil {
Log("C", "dpiVar_setNumElementsInArray", "i", i, "n", n)
}
if C.dpiVar_setNumElementsInArray(dv, C.uint32_t(n)) == C.DPI_FAILURE {
return errors.Errorf("%+v.setNumElementsInArray[%d](%d): %w", dv, i, n, st.getError())
}
}
//fmt.Println("n:", len(st.data[i]))
if err := info.set(dv, data, value); err != nil {
return err
}
}
if !named {
for i, v := range st.vars {
//if Log != nil {Log("C", "dpiStmt_bindByPos", "dpiStmt", st.dpiStmt, "i", i, "v", v) }
if C.dpiStmt_bindByPos(st.dpiStmt, C.uint32_t(i+1), v) == C.DPI_FAILURE {
return errors.Errorf("bindByPos[%d]: %w", i, st.getError())
}
}
return nil
}
for i, a := range args {
name := a.Name
if name == "" {
name = strconv.Itoa(a.Ordinal)
}
//fmt.Printf("bindByName(%q)\n", name)
cName := C.CString(name)
res := C.dpiStmt_bindByName(st.dpiStmt, cName, C.uint32_t(len(name)), st.vars[i])
C.free(unsafe.Pointer(cName))
if res == C.DPI_FAILURE {
return errors.Errorf("bindByName[%q]: %w", name, st.getError())
}
}
return nil
}
func (st *statement) bindVarTypeSwitch(info *argInfo, get *dataGetter, value interface{}) (interface{}, error) {
nilPtr := false
if Log != nil {
Log("msg", "bindVarTypeSwitch", "info", info, "value", fmt.Sprintf("[%T]%v", value, value))
}
vlr, isValuer := value.(driver.Valuer)
switch value.(type) {
case *driver.Rows:
default:
rv := reflect.ValueOf(value)
kind := rv.Kind()
if kind == reflect.Ptr {
if nilPtr = rv.IsNil(); nilPtr {
info.set = dataSetNull
value = reflect.Zero(rv.Type().Elem()).Interface()
} else {
value = rv.Elem().Interface()
}
}
}
switch v := value.(type) {
case Lob, []Lob:
info.typ, info.natTyp = C.DPI_ORACLE_TYPE_BLOB, C.DPI_NATIVE_TYPE_LOB
var isClob bool
switch v := v.(type) {
case Lob:
isClob = v.IsClob
case []Lob:
isClob = len(v) > 0 && v[0].IsClob
}
if isClob {
info.typ = C.DPI_ORACLE_TYPE_CLOB
}
info.set = st.dataSetLOB
if info.isOut {
*get = st.dataGetLOB
}
case *driver.Rows:
info.typ, info.natTyp = C.DPI_ORACLE_TYPE_STMT, C.DPI_NATIVE_TYPE_STMT
info.set = dataSetNull
if info.isOut {
*get = st.dataGetStmt
}
case int, []int:
info.typ, info.natTyp = C.DPI_ORACLE_TYPE_NUMBER, C.DPI_NATIVE_TYPE_INT64
if !nilPtr {
info.set = dataSetNumber
if info.isOut {
*get = dataGetNumber
}
}
case int32, []int32:
info.typ, info.natTyp = C.DPI_ORACLE_TYPE_NATIVE_INT, C.DPI_NATIVE_TYPE_INT64
if !nilPtr {
info.set = dataSetNumber
if info.isOut {
*get = dataGetNumber
}
}
case int64, []int64, sql.NullInt64, []sql.NullInt64:
info.typ, info.natTyp = C.DPI_ORACLE_TYPE_NUMBER, C.DPI_NATIVE_TYPE_INT64
if !nilPtr {
info.set = dataSetNumber
if info.isOut {
*get = dataGetNumber
}
}
case uint, []uint:
info.typ, info.natTyp = C.DPI_ORACLE_TYPE_NUMBER, C.DPI_NATIVE_TYPE_UINT64
if !nilPtr {
info.set = dataSetNumber
if info.isOut {
*get = dataGetNumber
}
}
case uint32, []uint32:
info.typ, info.natTyp = C.DPI_ORACLE_TYPE_NATIVE_UINT, C.DPI_NATIVE_TYPE_UINT64
if !nilPtr {
info.set = dataSetNumber
if info.isOut {
*get = dataGetNumber
}
}
case uint64, []uint64:
info.typ, info.natTyp = C.DPI_ORACLE_TYPE_NUMBER, C.DPI_NATIVE_TYPE_UINT64
if !nilPtr {
info.set = dataSetNumber
if info.isOut {
*get = dataGetNumber
}
}
case float32, []float32:
info.typ, info.natTyp = C.DPI_ORACLE_TYPE_NATIVE_FLOAT, C.DPI_NATIVE_TYPE_FLOAT
if !nilPtr {
info.set = dataSetNumber
if info.isOut {
*get = dataGetNumber
}
}
case float64, []float64:
info.typ, info.natTyp = C.DPI_ORACLE_TYPE_NATIVE_DOUBLE, C.DPI_NATIVE_TYPE_DOUBLE
if !nilPtr {
info.set = dataSetNumber
if info.isOut {
*get = dataGetNumber
}
}
case sql.NullFloat64, []sql.NullFloat64:
info.typ, info.natTyp = C.DPI_ORACLE_TYPE_NATIVE_DOUBLE, C.DPI_NATIVE_TYPE_DOUBLE
info.set = dataSetNumber
if info.isOut {
*get = dataGetNumber
}
case bool, []bool:
if st.dpiStmtInfo.isPLSQL == 1 || st.stmtOptions.boolString.IsZero() || st.PlSQLArrays() {
info.typ, info.natTyp = C.DPI_ORACLE_TYPE_BOOLEAN, C.DPI_NATIVE_TYPE_BOOLEAN
info.set = dataSetBool
if info.isOut {
*get = dataGetBool
}
} else {
info.typ, info.natTyp = C.DPI_ORACLE_TYPE_VARCHAR, C.DPI_NATIVE_TYPE_BYTES
info.bufSize = st.stmtOptions.boolString.MaxLen()
info.set = st.dataSetBoolBytes
if info.isOut {
*get = st.dataGetBoolBytes
}
}
case []byte, [][]byte:
info.typ, info.natTyp = C.DPI_ORACLE_TYPE_RAW, C.DPI_NATIVE_TYPE_BYTES
switch v := v.(type) {
case []byte:
info.bufSize = len(v)
case [][]byte:
for _, b := range v {
if n := len(b); n > info.bufSize {
info.bufSize = n
}
}
}
info.set = dataSetBytes
if info.isOut {
info.bufSize = 32767
*get = dataGetBytes
}
case Number, []Number:
info.typ, info.natTyp = C.DPI_ORACLE_TYPE_NUMBER, C.DPI_NATIVE_TYPE_BYTES
switch v := v.(type) {
case Number:
info.bufSize = len(v)
case []Number:
for _, s := range v {
if n := len(s); n > info.bufSize {
info.bufSize = n
}
}
}
info.set = dataSetBytes
if info.isOut {
info.bufSize = 32767
*get = dataGetBytes
}
case string, []string, nil:
info.typ, info.natTyp = C.DPI_ORACLE_TYPE_VARCHAR, C.DPI_NATIVE_TYPE_BYTES
switch v := v.(type) {
case string:
info.bufSize = 4 * len(v)
case []string:
for _, s := range v {
if n := 4 * len(s); n > info.bufSize {
info.bufSize = n
}
}
}
info.set = dataSetBytes
if info.isOut {
info.bufSize = 32767
*get = dataGetBytes
}
case time.Time, []time.Time, NullTime, []NullTime:
info.typ, info.natTyp = C.DPI_ORACLE_TYPE_DATE, C.DPI_NATIVE_TYPE_TIMESTAMP
info.set = st.conn.dataSetTime
if info.isOut {
*get = st.conn.dataGetTime
}
case time.Duration, []time.Duration:
info.typ, info.natTyp = C.DPI_ORACLE_TYPE_INTERVAL_DS, C.DPI_NATIVE_TYPE_INTERVAL_DS
info.set = st.conn.dataSetIntervalDS
if info.isOut {
*get = st.conn.dataGetIntervalDS
}
case Object:
info.objType = v.ObjectType.dpiObjectType
info.typ, info.natTyp = C.DPI_ORACLE_TYPE_OBJECT, C.DPI_NATIVE_TYPE_OBJECT
info.set = st.dataSetObject
if info.isOut {
*get = st.dataGetObject
}
case *Object:
if !nilPtr && v != nil {
info.objType = v.ObjectType.dpiObjectType
info.typ, info.natTyp = C.DPI_ORACLE_TYPE_OBJECT, C.DPI_NATIVE_TYPE_OBJECT
}
info.set = st.dataSetObject
if info.isOut {
*get = st.dataGetObject
}
case userType:
info.objType = v.ObjectRef().ObjectType.dpiObjectType
info.typ, info.natTyp = C.DPI_ORACLE_TYPE_OBJECT, C.DPI_NATIVE_TYPE_OBJECT
info.set = st.dataSetObject
if info.isOut {
*get = st.dataGetObject
}
default:
if !isValuer {
return value, errors.Errorf("unknown type %T", value)
}
var err error
if value, err = vlr.Value(); err != nil {
return value, errors.Errorf("arg.Value(): %w", err)
}
return st.bindVarTypeSwitch(info, get, value)
}
return value, nil
}
type dataSetter func(dv *C.dpiVar, data []C.dpiData, vv interface{}) error
func dataSetNull(dv *C.dpiVar, data []C.dpiData, vv interface{}) error {
for i := range data {
data[i].isNull = 1
}
return nil
}
func dataGetBool(v interface{}, data []C.dpiData) error {
if b, ok := v.(*bool); ok {
if len(data) == 0 || data[0].isNull == 1 {
*b = false
return nil
}
*b = C.dpiData_getBool(&data[0]) == 1
return nil
}
slice := v.(*[]bool)
if cap(*slice) >= len(data) {
*slice = (*slice)[:len(data)]
} else {
*slice = make([]bool, len(data))
}
for i := range data {
if data[i].isNull == 1 {
(*slice)[i] = false
continue
}
(*slice)[i] = C.dpiData_getBool(&data[i]) == 1
}
return nil
}
func dataSetBool(dv *C.dpiVar, data []C.dpiData, vv interface{}) error {
if vv == nil {
return dataSetNull(dv, data, nil)
}
b := C.int(0)
if v, ok := vv.(bool); ok {
if v {
b = 1
}
C.dpiData_setBool(&data[0], b)
return nil
}
if bb, ok := vv.([]bool); ok {
for i, v := range bb {
if v {
b = 1
}
C.dpiData_setBool(&data[i], b)
}
return nil
}
for i := range data {
data[i].isNull = 1
}
return nil
}
var _ = sql.Scanner((*NullTime)(nil))
func (c *conn) dataGetTime(v interface{}, data []C.dpiData) error {
switch x := v.(type) {
case *time.Time:
if len(data) == 0 || data[0].isNull == 1 {
*x = time.Time{}
return nil
}
c.dataGetTimeC(x, &data[0])
case *NullTime:
if x.Valid = !(len(data) == 0 || data[0].isNull == 1); x.Valid {
c.dataGetTimeC(&x.Time, &data[0])
}
case *[]time.Time:
n := len(data)
if cap(*x) >= n {
*x = (*x)[:n]
} else {
*x = make([]time.Time, n)
}
for i := range data {
c.dataGetTimeC(&((*x)[i]), &data[i])
}
case *[]NullTime:
n := len(data)
if cap(*x) >= n {
*x = (*x)[:n]
} else {
*x = make([]NullTime, n)
}
for i := range data {
if (*x)[i].Valid = !(data[i].isNull == 1); (*x)[i].Valid {
c.dataGetTimeC(&((*x)[i].Time), &data[i])
}
}
}
return nil
}
func (c *conn) dataGetTimeC(t *time.Time, data *C.dpiData) {
if data.isNull == 1 {
*t = time.Time{}
return
}
ts := C.dpiData_getTimestamp(data)
tz := c.params.Timezone
if ts.tzHourOffset != 0 || ts.tzMinuteOffset != 0 {
tz = timeZoneFor(ts.tzHourOffset, ts.tzMinuteOffset)
}
*t = 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,
)
}
func (c *conn) dataSetTime(dv *C.dpiVar, data []C.dpiData, vv interface{}) error {
if vv == nil {
return dataSetNull(dv, data, nil)
}
times := []time.Time{{}}
switch x := vv.(type) {
case time.Time:
times[0] = x
data[0].isNull = C.int(b2i(x.IsZero()))
case NullTime:
if data[0].isNull = C.int(b2i(!x.Valid)); x.Valid {
times[0] = x.Time
}
case []time.Time:
times = x
for i, t := range times {
data[i].isNull = C.int(b2i(t.IsZero()))
}
case []NullTime:
if cap(times) < len(x) {
times = make([]time.Time, len(x))
} else {
times = times[:len(x)]
}
for i, n := range x {
if data[i].isNull = C.int(b2i(!n.Valid)); n.Valid {
times[i] = x[i].Time
}
}
default:
for i := range data {
data[i].isNull = 1
}
return nil
}
tzHour, tzMin := C.int8_t(c.tzOffSecs/3600), C.int8_t((c.tzOffSecs%3600)/60)
for i, t := range times {
if data[i].isNull == 1 {
continue
}
t = t.In(c.params.Timezone)
Y, M, D := t.Date()
h, m, s := t.Clock()
C.dpiData_setTimestamp(&data[i],
C.int16_t(Y), C.uint8_t(M), C.uint8_t(D),
C.uint8_t(h), C.uint8_t(m), C.uint8_t(s), C.uint32_t(t.Nanosecond()),
tzHour, tzMin,
)
}
return nil
}
func (c *conn) dataGetIntervalDS(v interface{}, data []C.dpiData) error {
if Log != nil {
Log("msg", "dataGetIntervalDS", "data", data, "v", v)
}
switch x := v.(type) {
case *time.Duration:
if len(data) == 0 || data[0].isNull == 1 {
*x = 0
return nil
}
dataGetIntervalDS(x, &data[0])
case *[]time.Duration:
n := len(data)
if cap(*x) >= n {
*x = (*x)[:n]
} else {
*x = make([]time.Duration, n)
}
for i := range data {
dataGetIntervalDS(&((*x)[i]), &data[i])
}
}
return nil
}
func dataGetIntervalDS(t *time.Duration, d *C.dpiData) {
ds := C.dpiData_getIntervalDS(d)
*t = time.Duration(ds.days)*24*time.Hour +
time.Duration(ds.hours)*time.Hour +
time.Duration(ds.minutes)*time.Minute +
time.Duration(ds.seconds)*time.Second +
time.Duration(ds.fseconds)
if Log != nil {
Log("msg", "dataGetIntervalDS", "d", *d, "t", *t)
}
}
func (c *conn) dataSetIntervalDS(dv *C.dpiVar, data []C.dpiData, vv interface{}) error {
if vv == nil {
return dataSetNull(dv, data, nil)
}
times := []time.Duration{0}
switch x := vv.(type) {
case time.Duration:
times[0] = x
data[0].isNull = C.int(b2i(x == 0))
case []time.Duration:
times = x
for i, t := range times {
data[i].isNull = C.int(b2i(t == 0))
}
default:
for i := range data {
data[i].isNull = 1
}
return nil
}
if Log != nil {
Log("msg", "dataSetIntervalDS", "data", data, "times", times)
}
for i, t := range times {
if data[i].isNull == 1 {
continue
}
rem := t % (24 * time.Hour)
d := C.int32_t(t / (24 * time.Hour))
t, rem = rem, t%(time.Hour)
h := C.int32_t(t / time.Hour)
t, rem = rem, t%(time.Minute)
m := C.int32_t(t / time.Minute)
t, rem = rem, t%time.Second
s := C.int32_t(t / time.Second)
fs := C.int32_t(rem)
if Log != nil {
Log("i", i, "t", t, "day", d, "hour", h, "minute", m, "second", s, "fsecond", fs)
}
C.dpiData_setIntervalDS(&data[i], d, h, m, s, fs)
if Log != nil {
Log("i", i, "t", t, "data", data[i])
}
}
return nil
}
func dataGetNumber(v interface{}, data []C.dpiData) error {
switch x := v.(type) {
case *int:
if len(data) == 0 || data[0].isNull == 1 {
*x = 0
} else {
*x = int(C.dpiData_getInt64(&data[0]))
}
case *[]int:
*x = (*x)[:0]
for i := range data {
if data[i].isNull == 1 {
*x = append(*x, 0)
} else {
*x = append(*x, int(C.dpiData_getInt64(&data[i])))
}
}
case *int32:
if len(data) == 0 || data[0].isNull == 1 {
*x = 0
} else {
*x = int32(C.dpiData_getInt64(&data[0]))
}
case *[]int32:
*x = (*x)[:0]
for i := range data {
if data[i].isNull == 1 {
*x = append(*x, 0)
} else {
*x = append(*x, int32(C.dpiData_getInt64(&data[i])))
}
}
case *int64:
if len(data) == 0 || data[0].isNull == 1 {
*x = 0
} else {
*x = int64(C.dpiData_getInt64(&data[0]))
}
case *[]int64:
*x = (*x)[:0]
for i := range data {
if data[i].isNull == 1 {
*x = append(*x, 0)
} else {
*x = append(*x, int64(C.dpiData_getInt64(&data[i])))
}
}
case *sql.NullInt64:
if len(data) == 0 || data[0].isNull == 1 {
x.Valid = false
} else {
x.Valid, x.Int64 = true, int64(C.dpiData_getInt64(&data[0]))
}
case *[]sql.NullInt64:
*x = (*x)[:0]
for i := range data {
if data[i].isNull == 1 {
*x = append(*x, sql.NullInt64{Valid: false})
} else {
*x = append(*x, sql.NullInt64{Valid: true,
Int64: int64(C.dpiData_getInt64(&data[i]))})
}
}
case *sql.NullFloat64:
if len(data) == 0 || data[0].isNull == 1 {
x.Valid = false
} else {
x.Valid, x.Float64 = true, float64(C.dpiData_getDouble(&data[0]))
}
case *[]sql.NullFloat64:
*x = (*x)[:0]
for i := range data {
if data[i].isNull == 1 {
*x = append(*x, sql.NullFloat64{Valid: false})
} else {
*x = append(*x, sql.NullFloat64{Valid: true, Float64: float64(C.dpiData_getDouble(&data[i]))})
}
}
case *uint:
if len(data) == 0 || data[0].isNull == 1 {
*x = 0
} else {
*x = uint(C.dpiData_getUint64(&data[0]))
}
case *[]uint:
*x = (*x)[:0]
for i := range data {
if data[i].isNull == 1 {
*x = append(*x, 0)
} else {
*x = append(*x, uint(C.dpiData_getUint64(&data[i])))
}
}
case *uint32:
if len(data) == 0 || data[0].isNull == 1 {
*x = 0
} else {
*x = uint32(C.dpiData_getUint64(&data[0]))
}
case *[]uint32:
*x = (*x)[:0]
for i := range data {
if data[i].isNull == 1 {
*x = append(*x, 0)
} else {
*x = append(*x, uint32(C.dpiData_getUint64(&data[i])))
}
}
case *uint64:
if len(data) == 0 || data[0].isNull == 1 {
*x = 0
} else {
*x = uint64(C.dpiData_getUint64(&data[0]))
}
case *[]uint64:
*x = (*x)[:0]
for i := range data {
if data[i].isNull == 1 {
*x = append(*x, 0)
} else {
*x = append(*x, uint64(C.dpiData_getUint64(&data[i])))
}
}
case *float32:
if len(data) == 0 || data[0].isNull == 1 {
*x = 0
} else {
*x = float32(C.dpiData_getFloat(&data[0]))
}
case *[]float32:
*x = (*x)[:0]
for i := range data {
if data[i].isNull == 1 {
*x = append(*x, 0)
} else {
*x = append(*x, float32(C.dpiData_getFloat(&data[i])))
}
}
case *float64:
if len(data) == 0 || data[0].isNull == 1 {
*x = 0
} else {
*x = float64(C.dpiData_getDouble(&data[0]))
}
case *[]float64:
*x = (*x)[:0]
for i := range data {
if data[i].isNull == 1 {
*x = append(*x, 0)
} else {
*x = append(*x, float64(C.dpiData_getDouble(&data[i])))
}
}
default:
return errors.Errorf("unknown number [%T] %#v", v, v)
}
//fmt.Printf("setInt64(%#v, %#v)\n", data, C.int64_t(int64(v.(int))))
return nil
}
func dataSetNumber(dv *C.dpiVar, data []C.dpiData, vv interface{}) error {
if len(data) == 0 {
return nil
}
if vv == nil {
return dataSetNull(dv, data, nil)
}
switch slice := vv.(type) {
case int:
i, x := 0, slice
C.dpiData_setInt64(&data[i], C.int64_t(x))
case []int:
for i, x := range slice {
C.dpiData_setInt64(&data[i], C.int64_t(x))
}
case int32:
i, x := 0, slice
C.dpiData_setInt64(&data[i], C.int64_t(x))
case []int32:
for i, x := range slice {
C.dpiData_setInt64(&data[i], C.int64_t(x))
}
case int64:
i, x := 0, slice
C.dpiData_setInt64(&data[i], C.int64_t(x))
case []int64:
for i, x := range slice {
C.dpiData_setInt64(&data[i], C.int64_t(x))
}
case sql.NullInt64:
i, x := 0, slice
if x.Valid {
data[i].isNull = 0
C.dpiData_setInt64(&data[i], C.int64_t(x.Int64))
} else {
data[i].isNull = 1
}
case []sql.NullInt64:
for i, x := range slice {
if x.Valid {
data[i].isNull = 0
C.dpiData_setInt64(&data[i], C.int64_t(x.Int64))
} else {
data[i].isNull = 1
}
}
case sql.NullFloat64:
i, x := 0, slice
if x.Valid {
data[i].isNull = 0
C.dpiData_setDouble(&data[i], C.double(x.Float64))
} else {
data[i].isNull = 1
}
case []sql.NullFloat64:
for i, x := range slice {
if x.Valid {
data[i].isNull = 0
C.dpiData_setDouble(&data[i], C.double(x.Float64))
} else {
data[i].isNull = 1
}
}
case uint:
i, x := 0, slice
C.dpiData_setUint64(&data[i], C.uint64_t(x))
case []uint:
for i, x := range slice {
C.dpiData_setUint64(&data[i], C.uint64_t(x))
}
case uint32:
i, x := 0, slice
C.dpiData_setUint64(&data[i], C.uint64_t(x))
case []uint32:
for i, x := range slice {
C.dpiData_setUint64(&data[i], C.uint64_t(x))
}
case uint64:
i, x := 0, slice
C.dpiData_setUint64(&data[i], C.uint64_t(x))
case []uint64:
for i, x := range slice {
C.dpiData_setUint64(&data[i], C.uint64_t(x))
}
case float32:
i, x := 0, slice
C.dpiData_setFloat(&data[i], C.float(x))
case []float32:
for i, x := range slice {
C.dpiData_setFloat(&data[i], C.float(x))
}
case float64:
i, x := 0, slice
C.dpiData_setDouble(&data[i], C.double(x))
case []float64:
for i, x := range slice {
C.dpiData_setDouble(&data[i], C.double(x))
}
default:
return errors.Errorf("unknown number slice [%T] %#v", vv, vv)
}
//fmt.Printf("setInt64(%#v, %#v)\n", data, C.int64_t(int64(v.(int))))
return nil
}
func dataGetBytes(v interface{}, data []C.dpiData) error {
switch x := v.(type) {
case *[]byte:
if len(data) == 0 || data[0].isNull == 1 {
*x = nil
return nil
}
db := C.dpiData_getBytes(&data[0])
b := ((*[32767]byte)(unsafe.Pointer(db.ptr)))[:db.length:db.length]
// b must be copied
*x = append((*x)[:0], b...)
case *[][]byte:
maX := (*x)[:cap(*x)]
*x = (*x)[:0]
for i := range data {
if data[i].isNull == 1 {
*x = append(*x, nil)
continue
}
db := C.dpiData_getBytes(&data[i])
b := ((*[32767]byte)(unsafe.Pointer(db.ptr)))[:db.length:db.length]
// b must be copied
if i < len(maX) {
*x = append(*x, append(maX[i][:0], b...))
} else {
*x = append(*x, append(make([]byte, 0, len(b)), b...))
}
}
case *Number:
if len(data) == 0 || data[0].isNull == 1 {
*x = ""
return nil
}
b := C.dpiData_getBytes(&data[0])
*x = Number(((*[32767]byte)(unsafe.Pointer(b.ptr)))[:b.length:b.length])
case *[]Number:
*x = (*x)[:0]
for i := range data {
if data[i].isNull == 1 {
*x = append(*x, "")
continue
}
b := C.dpiData_getBytes(&data[i])
*x = append(*x, Number(((*[32767]byte)(unsafe.Pointer(b.ptr)))[:b.length:b.length]))
}
case *string:
if len(data) == 0 || data[0].isNull == 1 {
*x = ""
return nil
}
b := C.dpiData_getBytes(&data[0])
*x = string(((*[32767]byte)(unsafe.Pointer(b.ptr)))[:b.length:b.length])
case *[]string:
*x = (*x)[:0]
for i := range data {
if data[i].isNull == 1 {
*x = append(*x, "")
continue
}
b := C.dpiData_getBytes(&data[i])
*x = append(*x, string(((*[32767]byte)(unsafe.Pointer(b.ptr)))[:b.length:b.length]))
}
case *interface{}:
switch y := (*x).(type) {
case []byte:
err := dataGetBytes(&y, data[:1])
*x = y
return err
case [][]byte:
err := dataGetBytes(&y, data)
*x = y
return err
case Number:
err := dataGetBytes(&y, data[:1])
*x = y
return err
case []Number:
err := dataGetBytes(&y, data)
*x = y
return err
case string:
err := dataGetBytes(&y, data[:1])
*x = y
return err
case []string:
err := dataGetBytes(&y, data)
*x = y
return err
default:
return errors.Errorf("awaited []byte/string/Number, got %T (%#v)", x, x)
}
default:
return errors.Errorf("awaited []byte/string/Number, got %T (%#v)", v, v)
}
return nil
}
func dataSetBytes(dv *C.dpiVar, data []C.dpiData, vv interface{}) error {
if len(data) == 0 {
return nil
}
if vv == nil {
return dataSetNull(dv, data, nil)
}
var p *C.char
switch slice := vv.(type) {
case []byte:
i, x := 0, slice
if len(x) == 0 {
data[i].isNull = 1
return nil
}
data[i].isNull = 0
p = (*C.char)(unsafe.Pointer(&x[0]))
//if Log != nil {Log("C", "dpiVar_setFromBytes", "dv", dv, "pos", pos, "p", p, "len", len(x)) }
C.dpiVar_setFromBytes(dv, C.uint32_t(i), p, C.uint32_t(len(x)))
case [][]byte:
for i, x := range slice {
if len(x) == 0 {
data[i].isNull = 1
continue
}
data[i].isNull = 0
p = (*C.char)(unsafe.Pointer(&x[0]))
//if Log != nil {Log("C", "dpiVar_setFromBytes", "dv", dv, "pos", pos, "p", p, "len", len(x)) }
C.dpiVar_setFromBytes(dv, C.uint32_t(i), p, C.uint32_t(len(x)))
}
case Number:
i, x := 0, slice
if len(x) == 0 {
data[i].isNull = 1
return nil
}
data[i].isNull = 0
dpiSetFromString(dv, C.uint32_t(i), string(x))
case []Number:
for i, x := range slice {
if len(x) == 0 {
data[i].isNull = 1
continue
}
data[i].isNull = 0
dpiSetFromString(dv, C.uint32_t(i), string(x))
}
case string:
i, x := 0, slice
if len(x) == 0 {
data[i].isNull = 1
return nil
}
data[i].isNull = 0
dpiSetFromString(dv, C.uint32_t(i), x)
case []string:
for i, x := range slice {
if len(x) == 0 {
data[i].isNull = 1
continue
}
data[i].isNull = 0
dpiSetFromString(dv, C.uint32_t(i), x)
}
default:
return errors.Errorf("awaited [][]byte/[]string/[]Number, got %T (%#v)", vv, vv)
}
return nil
}
func (st *statement) dataGetBoolBytes(v interface{}, data []C.dpiData) error {
switch x := v.(type) {
case *bool:
if len(data) == 0 || data[0].isNull == 1 {
*x = false
return nil
}
db := C.dpiData_getBytes(&data[0])
b := ((*[32767]byte)(unsafe.Pointer(db.ptr)))[:db.length:db.length]
*x = st.stmtOptions.boolString.FromString(string(b))
case *[]bool:
*x = (*x)[:0]
for i := range data {
if data[i].isNull == 1 {
*x = append(*x, false)
continue
}
db := C.dpiData_getBytes(&data[i])
b := ((*[32767]byte)(unsafe.Pointer(db.ptr)))[:db.length:db.length]
*x = append(*x, st.stmtOptions.boolString.FromString(string(b)))
}
case *interface{}:
switch y := (*x).(type) {
case bool:
err := st.dataGetBoolBytes(&y, data[:1])
*x = y
return err
case []bool:
err := st.dataGetBoolBytes(&y, data)
*x = y
return err
default:
return errors.Errorf("awaited bool, got %T (%#v)", x, x)
}
default:
return errors.Errorf("awaited bool, got %T (%#v)", v, v)
}
return nil
}
func (st *statement) dataSetBoolBytes(dv *C.dpiVar, data []C.dpiData, vv interface{}) error {
if len(data) == 0 {
return nil
}
if vv == nil {
return dataSetNull(dv, data, nil)
}
var p *C.char
switch slice := vv.(type) {
case bool:
i, x := 0, slice
data[i].isNull = 0
s := []byte(st.stmtOptions.boolString.ToString(x))
p = (*C.char)(unsafe.Pointer(&s[0]))
C.dpiVar_setFromBytes(dv, C.uint32_t(i), p, C.uint32_t(len(s)))
case []bool:
for i, x := range slice {
data[i].isNull = 0
s := []byte(st.stmtOptions.boolString.ToString(x))
p = (*C.char)(unsafe.Pointer(&s[0]))
C.dpiVar_setFromBytes(dv, C.uint32_t(i), p, C.uint32_t(len(s)))
}
default:
return errors.Errorf("awaited bool/[]bool, got %T (%#v)", vv, vv)
}
return nil
}
func (c *conn) dataGetStmt(v interface{}, data []C.dpiData) error {
if row, ok := v.(*driver.Rows); ok {
if len(data) == 0 || data[0].isNull == 1 {
*row = nil
return nil
}
return c.dataGetStmtC(row, &data[0])
}
rows := v.(*[]driver.Rows)
if cap(*rows) >= len(data) {
*rows = (*rows)[:len(data)]
} else {
*rows = make([]driver.Rows, len(data))
}
var firstErr error
for i := range data {
if err := c.dataGetStmtC(&((*rows)[i]), &data[i]); err != nil && firstErr == nil {
firstErr = err
}
}
return firstErr
}
func (c *conn) dataGetStmtC(row *driver.Rows, data *C.dpiData) error {
if data.isNull == 1 {
*row = nil
return nil
}
st := &statement{conn: c, dpiStmt: C.dpiData_getStmt(data)}
var n C.uint32_t
if C.dpiStmt_getNumQueryColumns(st.dpiStmt, &n) == C.DPI_FAILURE {
*row = &rows{
err: errors.Errorf("getNumQueryColumns: %w: %w", c.getError(), io.EOF),
}
return nil
}
var err error
*row, err = st.openRows(int(n))
return err
}
func (c *conn) dataGetLOB(v interface{}, data []C.dpiData) error {
if L, ok := v.(*Lob); ok {
if len(data) == 0 || data[0].isNull == 1 {
*L = Lob{}
return nil
}
c.dataGetLOBC(L, &data[0])
return nil
}
slice := v.(*[]Lob)
n := len(data)
if cap(*slice) >= n {
*slice = (*slice)[:n]
} else {
*slice = make([]Lob, n)
}
for i := range data {
c.dataGetLOBC(&((*slice)[i]), &data[i])
}
return nil
}
func (c *conn) dataGetLOBC(L *Lob, data *C.dpiData) {
L.Reader = nil
if data.isNull == 1 {
return
}
lob := C.dpiData_getLOB(data)
if lob == nil {
return
}
L.Reader = &dpiLobReader{conn: c, dpiLob: lob, IsClob: L.IsClob}
}
func (c *conn) dataSetLOB(dv *C.dpiVar, data []C.dpiData, vv interface{}) error {
if len(data) == 0 {
return nil
}
if vv == nil {
return dataSetNull(dv, data, nil)
}
lobs := []Lob{{}}
if L, ok := vv.(Lob); ok {
lobs[0] = L
} else {
lobs = vv.([]Lob)
}
var firstErr error
for i, L := range lobs {
//fmt.Printf("dataSetLob[%d]=(%T) %#v\n", i, L, L)
if L.Reader == nil {
data[i].isNull = 1
continue
}
data[i].isNull = 0
if r, ok := L.Reader.(*dpiLobReader); ok {
C.dpiVar_setFromLob(dv, C.uint32_t(i), r.dpiLob)
continue
}
typ := C.dpiOracleTypeNum(C.DPI_ORACLE_TYPE_BLOB)
if L.IsClob {
typ = C.DPI_ORACLE_TYPE_CLOB
}
var lob *C.dpiLob
if C.dpiConn_newTempLob(c.dpiConn, typ, &lob) == C.DPI_FAILURE {
return errors.Errorf("newTempLob(typ=%d): %w", typ, c.getError())
}
var chunkSize C.uint32_t
_ = C.dpiLob_getChunkSize(lob, &chunkSize)
if chunkSize == 0 {
chunkSize = 8192
}
for chunkSize < minChunkSize {
chunkSize <<= 1
}
lw := &dpiLobWriter{dpiLob: lob, conn: c, isClob: L.IsClob}
_, err := io.CopyBuffer(lw, L, make([]byte, int(chunkSize)))
//fmt.Printf("%p written %d with chunkSize=%d\n", lob, n, chunkSize)
if closeErr := lw.Close(); closeErr != nil {
if err == nil {
err = closeErr
}
//fmt.Printf("close %p: %+v\n", lob, closeErr)
}
C.dpiVar_setFromLob(dv, C.uint32_t(i), lob)
if err != nil && firstErr == nil {
firstErr = err
}
}
return firstErr
}
type userType interface {
ObjectRef() *Object
}
// ObjectScanner assigns a value from a database object
type ObjectScanner interface {
sql.Scanner
userType
}
// ObjectWriter update database object before binding
type ObjectWriter interface {
WriteObject() error
userType
}
func (c *conn) dataSetObject(dv *C.dpiVar, data []C.dpiData, vv interface{}) error {
//fmt.Printf("\ndataSetObject(dv=%+v, data=%+v, vv=%+v)\n", dv, data, vv)
if len(data) == 0 {
return nil
}
if vv == nil {
return dataSetNull(dv, data, nil)
}
objs := []Object{{}}
switch o := vv.(type) {
case Object:
objs[0] = o
case *Object:
objs[0] = *o
case []Object:
objs = o
case []*Object:
objs = make([]Object, len(o))
for i, x := range o {
objs[i] = *x
}
case ObjectWriter:
err := o.WriteObject()
if err != nil {
return err
}
objs[0] = *o.ObjectRef()
case []ObjectWriter:
for _, ut := range o {
err := ut.WriteObject()
if err != nil {
return err
}
objs = append(objs, *ut.ObjectRef())
}
case userType:
objs[0] = *o.ObjectRef()
case []userType:
for _, ut := range o {
objs = append(objs, *ut.ObjectRef())
}
}
for i, obj := range objs {
if obj.dpiObject == nil {
data[i].isNull = 1
continue
}
data[i].isNull = 0
if C.dpiVar_setFromObject(dv, C.uint32_t(i), obj.dpiObject) == C.DPI_FAILURE {
return errors.Errorf("setFromObject: %w", c.getError())
}
}
return nil
}
func (c *conn) dataGetObject(v interface{}, data []C.dpiData) error {
switch out := v.(type) {
case *Object:
d := Data{
ObjectType: out.ObjectType,
dpiData: data[0],
}
*out = *d.GetObject()
case ObjectScanner:
d := Data{
ObjectType: out.ObjectRef().ObjectType,
dpiData: data[0],
}
return out.Scan(d.GetObject())
default:
return fmt.Errorf("dataGetObject not implemented for type %T (maybe you need to implement the Scan method)", v)
}
return nil
}
// CheckNamedValue is called before passing arguments to the driver
// and is called in place of any ColumnConverter. CheckNamedValue must do type
// validation and conversion as appropriate for the driver.
//
// If CheckNamedValue returns ErrRemoveArgument, the NamedValue will not be included
// in the final query arguments.
// This may be used to pass special options to the query itself.
//
// If ErrSkip is returned the column converter error checking path is used
// for the argument.
// Drivers may wish to return ErrSkip after they have exhausted their own special cases.
func (st *statement) CheckNamedValue(nv *driver.NamedValue) error {
if nv == nil {
return nil
}
if apply, ok := nv.Value.(Option); ok {
if apply != nil {
apply(&st.stmtOptions)
}
return driver.ErrRemoveArgument
}
return nil
}
// ColumnConverter may be optionally implemented by Stmt
// if the statement is aware of its own columns' types and
// can convert from any type to a driver Value.
func (st *statement) ColumnConverter(idx int) driver.ValueConverter {
c := driver.ValueConverter(driver.DefaultParameterConverter)
switch col := st.columns[idx]; col.OracleType {
case C.DPI_ORACLE_TYPE_NUMBER:
switch col.NativeType {
case C.DPI_NATIVE_TYPE_INT64, C.DPI_NATIVE_TYPE_UINT64:
c = Int64
//case C.DPI_NATIVE_TYPE_FLOAT, C.DPI_NATIVE_TYPE_DOUBLE:
// c = Float64
default:
c = Num
}
}
if Log != nil {
Log("msg", "ColumnConverter", "c", c)
}
return driver.Null{Converter: c}
}
func (st *statement) openRows(colCount int) (*rows, error) {
sliceLen := st.FetchRowCount()
C.dpiStmt_setFetchArraySize(st.dpiStmt, C.uint32_t(sliceLen))
r := rows{
statement: st,
columns: make([]Column, colCount),
vars: make([]*C.dpiVar, colCount),
data: make([][]C.dpiData, colCount),
}
var info C.dpiQueryInfo
var ti C.dpiDataTypeInfo
for i := 0; i < colCount; i++ {
if C.dpiStmt_getQueryInfo(st.dpiStmt, C.uint32_t(i+1), &info) == C.DPI_FAILURE {
return nil, errors.Errorf("getQueryInfo[%d]: %w", i, st.getError())
}
ti = info.typeInfo
bufSize := int(ti.clientSizeInBytes)
//if Log != nil {Log("msg", "openRows", "col", i, "info", ti) }
//if Log != nil {Log("dNTN", int(ti.defaultNativeTypeNum), "number", C.DPI_ORACLE_TYPE_NUMBER) }
switch ti.oracleTypeNum {
case C.DPI_ORACLE_TYPE_NUMBER:
switch ti.defaultNativeTypeNum {
case C.DPI_NATIVE_TYPE_FLOAT, C.DPI_NATIVE_TYPE_DOUBLE:
ti.defaultNativeTypeNum = C.DPI_NATIVE_TYPE_BYTES
bufSize = 40
}
case C.DPI_ORACLE_TYPE_DATE,
C.DPI_ORACLE_TYPE_TIMESTAMP, C.DPI_ORACLE_TYPE_TIMESTAMP_TZ, C.DPI_ORACLE_TYPE_TIMESTAMP_LTZ:
ti.defaultNativeTypeNum = C.DPI_NATIVE_TYPE_TIMESTAMP
case C.DPI_ORACLE_TYPE_BLOB:
if !st.LobAsReader() {
ti.oracleTypeNum = C.DPI_ORACLE_TYPE_LONG_RAW
ti.defaultNativeTypeNum = C.DPI_NATIVE_TYPE_BYTES
}
case C.DPI_ORACLE_TYPE_CLOB:
if !st.LobAsReader() {
ti.oracleTypeNum = C.DPI_ORACLE_TYPE_LONG_VARCHAR
ti.defaultNativeTypeNum = C.DPI_NATIVE_TYPE_BYTES
}
}
r.columns[i] = Column{
Name: C.GoStringN(info.name, C.int(info.nameLength)),
OracleType: ti.oracleTypeNum,
NativeType: ti.defaultNativeTypeNum,
Size: ti.clientSizeInBytes,
Precision: ti.precision,
Scale: ti.scale,
Nullable: info.nullOk == 1,
ObjectType: ti.objectType,
SizeInChars: ti.sizeInChars,
DBSize: ti.dbSizeInBytes,
}
var err error
//fmt.Printf("%d. %+v\n", i, r.columns[i])
vi := varInfo{
Typ: ti.oracleTypeNum,
NatTyp: ti.defaultNativeTypeNum,
ObjectType: ti.objectType,
BufSize: bufSize,
SliceLen: sliceLen,
}
if r.vars[i], r.data[i], err = st.newVar(vi); err != nil {
return nil, err
}
if C.dpiStmt_define(st.dpiStmt, C.uint32_t(i+1), r.vars[i]) == C.DPI_FAILURE {
return nil, errors.Errorf("define[%d]: %w", i, st.getError())
}
}
if C.dpiStmt_addRef(st.dpiStmt) == C.DPI_FAILURE {
return &r, errors.Errorf("dpiStmt_addRef: %w", st.getError())
}
st.columns = r.columns
return &r, nil
}
// Column holds the info from a column.
type Column struct {
Name string
ObjectType *C.dpiObjectType
OracleType C.dpiOracleTypeNum
NativeType C.dpiNativeTypeNum
Size, SizeInChars, DBSize C.uint32_t
Precision C.int16_t
Scale C.int8_t
Nullable bool
}
func dpiSetFromString(dv *C.dpiVar, pos C.uint32_t, x string) {
C.godror_setFromString(dv, pos, x)
}
var stringBuilders = stringBuilderPool{
p: &sync.Pool{New: func() interface{} { return &strings.Builder{} }},
}
type stringBuilderPool struct {
p *sync.Pool
}
func (sb stringBuilderPool) Get() *strings.Builder {
return sb.p.Get().(*strings.Builder)
}
func (sb *stringBuilderPool) Put(b *strings.Builder) {
b.Reset()
sb.p.Put(b)
}
/*
// ResetSession is called while a connection is in the connection
// pool. No queries will run on this connection until this method returns.
//
// If the connection is bad this should return driver.ErrBadConn to prevent
// the connection from being returned to the connection pool. Any other
// error will be discarded.
func (c *conn) ResetSession(ctx context.Context) error {
if Log != nil {
Log("msg", "ResetSession", "conn", c.dpiConn)
}
//subCtx, cancel := context.WithTimeout(ctx, 30*time.Second)
//err := c.Ping(subCtx)
//cancel()
return c.Ping(ctx)
}
*/
1
https://gitee.com/tdus/godror.git
git@gitee.com:tdus/godror.git
tdus
godror
godror
master

搜索帮助