From 6e2ca410e90cdf85fcd1122f1849543b5d0b7b2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?No=C3=A9mi=20V=C3=A1nyi?= Date: Wed, 22 Jan 2020 14:07:31 +0100 Subject: [PATCH] Update github.com/godror/godror to v0.10.4 (#15737) ## What does this PR do? This PR updates the dependency `github.com/godror/godror` to v0.10.4. ## Why is it important? Packaging of Metricbeat fails due to the issue in the `godror` version we are currently using. See more about the problem: https://github.com/godror/godror/issues/8 --- NOTICE.txt | 4 +- vendor/github.com/godror/godror/CHANGELOG.md | 305 +--------- vendor/github.com/godror/godror/conn.go | 115 ++-- .../godror/godror/contrib/free.db/reset.sql | 15 + vendor/github.com/godror/godror/data.go | 82 ++- vendor/github.com/godror/godror/drv.go | 557 +++++++----------- vendor/github.com/godror/godror/go.mod | 2 +- vendor/github.com/godror/godror/obj.go | 154 +++-- vendor/github.com/godror/godror/orahlp.go | 199 +++++++ vendor/github.com/godror/godror/queue.go | 24 +- vendor/github.com/godror/godror/stmt.go | 23 +- vendor/github.com/godror/godror/subscr.go | 28 +- vendor/github.com/godror/godror/version.go | 4 +- vendor/vendor.json | 10 +- 14 files changed, 696 insertions(+), 826 deletions(-) create mode 100644 vendor/github.com/godror/godror/contrib/free.db/reset.sql diff --git a/NOTICE.txt b/NOTICE.txt index 08a37d10a5b..5e3a26acd96 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -2101,8 +2101,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------- Dependency: github.com/godror/godror -Version: v0.9.0 -Revision: 446a29355d2967f04604e8e015a3343917860adf +Version: v0.10.4 +Revision: 0123d49bd73e1bed106ac8b6af67f943fbbf06e2 License type (autodetected): Apache-2.0 ./vendor/github.com/godror/godror/LICENSE.md: -------------------------------------------------------------------- diff --git a/vendor/github.com/godror/godror/CHANGELOG.md b/vendor/github.com/godror/godror/CHANGELOG.md index 0541bbe23b5..72e335fffd1 100644 --- a/vendor/github.com/godror/godror/CHANGELOG.md +++ b/vendor/github.com/godror/godror/CHANGELOG.md @@ -5,307 +5,14 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). ## [Unreleased] -### Added - -## [2.24.0] -### Changed -- License: SPDX-License-Identifier: UPL-1.0 OR APL-2.0 - -## [2.23.5] -### Changed -- ODPI-C v3.3.0 - -## [2.23.2] -### Changed -- Close statement (and dpiStmt) on Break/bad conn. - -## [2.23.0] -### Added -- Conn.Timezone() returns the connection's timezone -- allow setting the timezone with Timezone ConnectionParam. -- Support to change password with NewPassword ConnectionParams. - -### Changed -- set DefaultEnqOptions and DefaultDeqOptions in NewQueue. -- make ConnectionParams.WaitTimeout, SessionMaxLifeTime, SessionTimeout be time.Duration. - -## [2.21.3] - 2019-10-03 -### Added -- Export Queue.PayloadObjectType -- Queue.SetEnqOptions, Queue.SetDeqOptions, Queue.SetDeqCorrelation - -## [2.21.1] - 2019-10-02 -### Changed -- Really close the connection if it's bad. For #194. -- ODPI-C v3.2.2 -- de-embed conn from Queue. - -## [2.20.1] - 2019-09-05 -### Added -- AsOraErr function - -### Changed -- Object.reset set attributes to null. -- Use golang.org/x/xerrors instead of github.com/pkg/errors. -- ObjectType.Close became unexported. - -## [2.20.0] - 2019-09-04 -### Added -- ObjectType cache in connection. -- Queue support with Objects. - -### Changed -- ObjectType.Close became unexported. -- Change Object Set/Get -- use golang.org/x/xerrors instead of github.com/pkg/errors. - -## [2.19.0] - 2019-08-15 -### Changed -- Require Context for getConn and thus in ClientVersion, ServerVersion, GetObjectType, DriverConn functions. - -## [2.18.5] - 2019-08-14 -### Changed -- Remove log.Println left in... - -## [2.18.4] - 2019-08-14 -### Changed -- Timezone detection: DBTIMEZONE is plain wrong, parse from SYSTIMESTAMP. - -## [2.18.3] - 2019-08-13 -### Changed -- GetObjectType uppercases the name by default. -- Upgrade to ODPI-C v3.2.1 - -## [2.18.2] - 2019-07-23 -### Changed -- Force copying of bytes (garbage appears Out with RAW). - -## [2.18.0] - 2019-07-16 -### Added -- Setable pool session timeouts. - -## [2.16.4] - 2019-06-26 -### Changed -- Fix bool input (#166). -- Allow region name from DBTIMEZONE, not just offset. - -## [2.16.2] - 2019-05-27 -### Changed -- Make Query AUTOCOMMIT like Exec - it's needed to release Rows for "FOR UPDATE". - -## [2.16.1] - 2019-05-27 -### Added -- Data.SetNull -- Expose dpiConn_newVar - -## [2.16.0] - 2019-05-17 -### Changed -- NumberAsString new option for #159. - -## [2.15.3] - 2019-05-16 -### Changed -- ParseConnString: reorder logic to allow 'sys/... as sysdba' (without @) - -## [2.15.3] - 2019-05-16 -### Changed -- ParseConnString: reorder logic to allow 'sys/... as sysdba' (without @) - -## [2.15.2] - 2019-05-12 -### Changed -- Use time.Local if it equals with DBTIMEZONE (use DST of time.Local). - -## [2.15.1] - 2019-05-09 -### Changed -- Fix heterogenous pools (broken with 2.14.1) - -## [2.15.0] - 2019-05-09 -### Added -- Implement dataGetObject to access custom user types -- Add ObjectScanner and ObjectWriter interfaces to provide a way to load/update values from/to a struct and database object type. - -## [2.14.2] - 2019-05-07 -### Added -- Cache timezone with the pool and in the conn struct, too. - -## [2.14.1] - 2019-05-07 -- Try to get the serve DBTIMEZONE, if fails use time.Local - -## [2.14.0] - 2019-05-07 -### Changed -- Default to time.Local in DATE types when sending to DB, too. - -## [2.13.2] - 2019-05-07 -### Changed -- Default to time.Local timezone for DATE types. - -## [2.13.1] - 2019-05-06 -### Changed -- Fix 'INTERVAL DAY TO SECOND' NULL case. - -## [2.12.8] - 2019-05-02 -### Added -- NewConnector, NewSessionIniter - -## [2.12.7] - 2019-04-24 -### Changed -- ODPI-C v3.1.4 (rowcount for PL/SQL block) - -## [2.12.6] - 2019-04-12 -### Added -- Allow calling with LOB got from DB, and don't copy it - see #135. - -## [2.12.5] - 2019-04-03 -### Added -- Make it compile under Go 1.9. - -## [2.12.4] - 2019-03-13 -## Added -- Upgrade to ODPI-C v3.1.3 - -## [2.12.3] - 2019-02-20 -### Changed -- Use ODPI-C v3.1.1 -### Added -- Make godror.drv implement driver.DriverContext with OpenConnector. - -## [2.12.2] - 2019-02-15 -### Changed -- Use ODPI-C v3.1.1 - -## [2.12.0] - 2019-01-21 -### Changed -- Use ODPI-C v3.1.0 - -## [2.11.2] - 2019-01-15 -### Changed -- ISOLATION LEVEL READ COMMITTED (typo) fix. - -## [2.11.1] - 2018-12-13 -### Changed -- Use C.dpiAuthMode, C.dpiStartupMode, C.dpiShutdownMode instead of C.uint - for #129. - -## [2.11.0] - 2018-12-13 -### Changed -- Do not set empty SID from ORACLE_SID/TWO_TASK environment variables, leave it to ODPI. - -### Added -- Allow PRELIM authentication to allow Startup and Shutdown. - -## [2.10.1] - 2018-11-23 -### Changed -- Don't call SET TRANSACTION if not really needed in BeginTx - if the isolation level hasn't changed. - -## [2.10.0] - 2018-11-18 -### Added -- Implement RowsNextResultSet to return implicit result sets set by DBMS_SQL.return. -- Allow using heterogeneous pools with user set with ContextWithUserPassw. - -## [2.9.1] - 2018-11-14 -### Added -- allow RETURNING with empty result set (such as UPDATE). -- Allow SELECT to return object types. - -### Changed -- fixed Number.MarshalJSON (see #112)' - -## [2.9.0] - 2018-10-12 -### Changed -- The default type for BLOB is []byte and for CLOB is a string - no need for ClobAsString() option. - -## [2.8.2] - 2018-10-01 -### Changed -- Fix the driver.Valuer handling, make it the last resort - -## [2.8.1] - 2018-09-27 -### Added -- CallTimeout option to set a per-statement OCI_ATTR_CALL_TIMEOUT. -- Allow login with " AS SYSASM", as requested in #100. - -### Changed -- Hash the password ("SECRET-sasdas=") in ConnectionParams.String(). - -## [2.8.0] - 2018-09-21 -### Added -- WrapRows wraps a driver.Rows (such as a returned cursor from a stored procedure) as an sql.Rows for easier handling. - -### Changed -- Do not allow events by default, make them opt-in with EnableEvents connection parameter - see #98. - -## [2.7.1] - 2018-09-17 -### Changed -- Inherit parent statement's Options for statements returned as sql.Out. - -## [2.7.0] - 2018-09-14 -### Changed -- Update ODPI-C to v3.0.0. - -## [2.6.0] - 2018-08-31 -### Changed -- convert named types to their underlying scalar values - see #96, using MagicTypeConversion() option. - -## [2.5.11] - 2018-08-30 -### Added -- Allow driver.Valuer as Query argument - see #94. - -## [2.5.10] - 2018-08-26 -### Changed -- use sergeymakinen/oracle-instant-client:12.2 docker for tests -- added ODPI-C and other licenses into LICENSE.md -- fill varInfo.ObjectType for better Object support - -## [2.5.9] - 2018-08-03 -### Added -- add CHANGELOG -- check that `len(dest) == len(rows.columns)` in `rows.Next(dest)` - -### Changed -- after a Break, don't release a stmt, that may fail with SIGSEGV - see #84. - -## [2.5.8] - 2018-07-27 -### Changed -- noConnectionPooling option became standaloneConnection - -## [2.5.7] - 2018-07-25 -### Added -- noConnectionPooling option to force not using a session pool - -## [2.5.6] - 2018-07-18 -### Changed -- use ODPI-C v2.4.2 -- remove all logging/printing of passwords - -## [2.5.5] - 2018-07-03 -### Added -- allow *int with nil value to be used as NULL - -## [2.5.4] - 2018-06-29 -### Added -- allow ReadOnly transactions - -## [2.5.3] - 2018-06-29 -### Changed -- decrease maxArraySize to be compilable on 32-bit architectures. - -### Removed -- remove C struct size Printf - -## [2.5.2] - 2018-06-22 -### Changed -- fix liveness check in statement.Close - -## [2.5.1] - 2018-06-15 -### Changed -- sid -> service_name in docs -- travis: 1.10.3 -- less embedding of structs, clearer API docs +## [0.10.0] ### Added -- support RETURNING from DML -- set timeouts on poolCreateParams +- onInit parameter in the connection url (and OnInit in ConnectionParams) +- export Drv to be able to register new driver wrapping *godror.Drv. +- ContextWithUserPassw requires a connClass argument, too. -## [2.5.0] - 2018-05-15 +## [0.9.2] ### Changed -- update ODPI-C to v2.4.0 -- initialize context / load lib only on first Open, to allow import without Oracle Client installed -- use golangci-lint +- Make Data embed dpiData, not *dpiData diff --git a/vendor/github.com/godror/godror/conn.go b/vendor/github.com/godror/godror/conn.go index f1476349729..2f5db308c2d 100644 --- a/vendor/github.com/godror/godror/conn.go +++ b/vendor/github.com/godror/godror/conn.go @@ -33,7 +33,7 @@ const wrapResultset = "--WRAP_RESULTSET--" // with 32-bit platforms. The size of a `C.dpiData` is 32 Byte on a 64-bit system, `C.dpiSubscrMessageTable` is 40 bytes. // So this is 2^25. // See https://github.com/go-godror/godror/issues/73#issuecomment-401281714 -const maxArraySize = (1<<32)/C.sizeof_dpiSubscrMessageTable - 1 +const maxArraySize = (1<<30)/C.sizeof_dpiSubscrMessageTable - 1 var _ = driver.Conn((*conn)(nil)) var _ = driver.ConnBeginTx((*conn)(nil)) @@ -43,19 +43,19 @@ var _ = driver.Pinger((*conn)(nil)) //var _ = driver.ExecerContext((*conn)(nil)) type conn struct { - connParams ConnectionParams currentTT TraceTag + connParams ConnectionParams Client, Server VersionInfo tranParams tranParams - sync.RWMutex - currentUser string - *drv - dpiConn *C.dpiConn - inTransaction bool - newSession bool - timeZone *time.Location - tzOffSecs int - objTypes map[string]ObjectType + mu sync.RWMutex + currentUser string + drv *drv + dpiConn *C.dpiConn + timeZone *time.Location + objTypes map[string]ObjectType + tzOffSecs int + inTransaction bool + newSession bool } func (c *conn) getError() error { @@ -66,8 +66,8 @@ func (c *conn) getError() error { } func (c *conn) Break() error { - c.RLock() - defer c.RUnlock() + c.mu.RLock() + defer c.mu.RUnlock() if Log != nil { Log("msg", "Break", "dpiConn", c.dpiConn) } @@ -77,6 +77,8 @@ func (c *conn) Break() error { return nil } +func (c *conn) ClientVersion() (VersionInfo, error) { return c.drv.ClientVersion() } + // Ping checks the connection's state. // // WARNING: as database/sql calls database/sql/driver.Open when it needs @@ -90,8 +92,8 @@ func (c *conn) Ping(ctx context.Context) error { if err := c.ensureContextUser(ctx); err != nil { return err } - c.RLock() - defer c.RUnlock() + c.mu.RLock() + defer c.mu.RUnlock() done := make(chan error, 1) go func() { defer close(done) @@ -136,8 +138,8 @@ func (c *conn) Close() error { if c == nil { return nil } - c.Lock() - defer c.Unlock() + c.mu.Lock() + defer c.mu.Unlock() return c.close(true) } @@ -160,7 +162,7 @@ func (c *conn) close(doNotReuse bool) error { continue } seen[nm] = struct{}{} - o.close() + o.close(doNotReuse) } if !doNotReuse { return nil @@ -252,19 +254,19 @@ func (c *conn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, e c.tranParams = todo } - c.RLock() + c.mu.RLock() inTran := c.inTransaction - c.RUnlock() + c.mu.RUnlock() if inTran { return nil, errors.New("already in transaction") } - c.Lock() + c.mu.Lock() c.inTransaction = true - c.Unlock() + c.mu.Unlock() if tt, ok := ctx.Value(traceTagCtxKey).(TraceTag); ok { - c.Lock() + c.mu.Lock() c.setTraceTag(tt) - c.Unlock() + c.mu.Unlock() } return c, nil } @@ -284,9 +286,9 @@ func (c *conn) PrepareContext(ctx context.Context, query string) (driver.Stmt, e return nil, err } if tt, ok := ctx.Value(traceTagCtxKey).(TraceTag); ok { - c.Lock() + c.mu.Lock() c.setTraceTag(tt) - c.Unlock() + c.mu.Unlock() } if query == getConnection { if Log != nil { @@ -299,8 +301,8 @@ func (c *conn) PrepareContext(ctx context.Context, query string) (driver.Stmt, e defer func() { C.free(unsafe.Pointer(cSQL)) }() - c.RLock() - defer c.RUnlock() + c.mu.RLock() + defer c.mu.RUnlock() var dpiStmt *C.dpiStmt if C.dpiConn_prepareStmt(c.dpiConn, 0, cSQL, C.uint32_t(len(query)), nil, 0, (**C.dpiStmt)(unsafe.Pointer(&dpiStmt)), @@ -316,7 +318,7 @@ func (c *conn) Rollback() error { return c.endTran(false) } func (c *conn) endTran(isCommit bool) error { - c.Lock() + c.mu.Lock() c.inTransaction = false c.tranParams = tranParams{} @@ -332,7 +334,7 @@ func (c *conn) endTran(isCommit bool) error { err = maybeBadConn(errors.Errorf("Rollback: %w", c.getError()), c) } } - c.Unlock() + c.mu.Unlock() //fmt.Printf("%p.%s\n", c, msg) return err } @@ -385,13 +387,43 @@ func (c *conn) ServerVersion() (VersionInfo, error) { return c.Server, nil } -func (c *conn) init() error { +func (c *conn) init(onInit []string) error { if c.Client.Version == 0 { var err error if c.Client, err = c.drv.ClientVersion(); err != nil { return err } } + + if err := c.initVersionTZ(); err != nil || len(onInit) == 0 || !c.newSession { + return err + } + if Log != nil { + Log("newSession", c.newSession, "onInit", onInit) + } + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Duration(len(onInit))*time.Second) + defer cancel() + if Log != nil { + Log("doOnInit", len(onInit)) + } + for _, qry := range onInit { + if Log != nil { + Log("onInit", qry) + } + st, err := c.PrepareContext(ctx, qry) + if err != nil { + return errors.Errorf("%s: %w", qry, err) + } + _, err = st.Exec(nil) //lint:ignore SA1019 - it's hard to use ExecContext here + st.Close() + if err != nil { + return errors.Errorf("%s: %w", qry, err) + } + } + return nil + } + +func (c *conn) initVersionTZ() error { if c.Server.Version == 0 { var v C.dpiVersionInfo var release *C.char @@ -690,18 +722,16 @@ const userpwCtxKey = ctxKey("userPw") // ContextWithUserPassw returns a context with the specified user and password, // to be used with heterogeneous pools. -func ContextWithUserPassw(ctx context.Context, user, password string) context.Context { - return context.WithValue(ctx, userpwCtxKey, [2]string{user, password}) +func ContextWithUserPassw(ctx context.Context, user, password, connClass string) context.Context { + return context.WithValue(ctx, userpwCtxKey, [3]string{user, password, connClass}) } func (c *conn) ensureContextUser(ctx context.Context) error { - if !c.connParams.HeterogeneousPool { + if !(c.connParams.HeterogeneousPool || c.connParams.StandaloneConnection) { return nil } - - var up [2]string - var ok bool - if up, ok = ctx.Value(userpwCtxKey).([2]string); !ok || up[0] == c.currentUser { + up, ok := ctx.Value(userpwCtxKey).([3]string) + if !ok || up[0] == c.currentUser { return nil } @@ -711,14 +741,7 @@ func (c *conn) ensureContextUser(ctx context.Context) error { } } - c.Lock() - defer c.Unlock() - - if err := c.acquireConn(up[0], up[1]); err != nil { - return err - } - - return c.init() + return c.acquireConn(up[0], up[1], up[2]) } // StartupMode for the database. diff --git a/vendor/github.com/godror/godror/contrib/free.db/reset.sql b/vendor/github.com/godror/godror/contrib/free.db/reset.sql new file mode 100644 index 00000000000..c8666143455 --- /dev/null +++ b/vendor/github.com/godror/godror/contrib/free.db/reset.sql @@ -0,0 +1,15 @@ +WHENEVER SQLERROR CONTINUE + +DROP USER test CASCADE; + +WHENEVER SQLERROR EXIT SQL.SQLCODE ROLLBACK + +CREATE USER test IDENTIFIED BY r97oUPimsmTOIcBaeeDF; +ALTER USER test QUOTA 100m ON data; +GRANT create session, create table, create type, create sequence, create synonym, create procedure, change notification TO test; +GRANT EXECUTE ON SYS.DBMS_AQ TO test; +GRANT EXECUTE ON SYS.DBMS_AQADM TO test; + +GRANT create user, drop user, alter user TO test; +GRANT connect TO test WITH admin option; +GRANT create session TO test WITH admin option; diff --git a/vendor/github.com/godror/godror/data.go b/vendor/github.com/godror/godror/data.go index 1d78c8abbae..c42f3ec189a 100644 --- a/vendor/github.com/godror/godror/data.go +++ b/vendor/github.com/godror/godror/data.go @@ -24,7 +24,8 @@ import ( // Data holds the data to/from Oracle. type Data struct { ObjectType ObjectType - dpiData *C.dpiData + dpiData C.dpiData + implicitObj bool NativeTypeNum C.dpiNativeTypeNum } @@ -35,22 +36,22 @@ func NewData(v interface{}) (*Data, error) { if v == nil { return nil, errors.Errorf("%s: %w", "nil type", ErrNotSupported) } - data := Data{dpiData: &C.dpiData{isNull: 1}} + data := Data{dpiData: C.dpiData{isNull: 1}} return &data, data.Set(v) } // IsNull returns whether the data is null. func (d *Data) IsNull() bool { - // Use of C.dpiData_getIsNull(d.dpiData) would be safer, + // Use of C.dpiData_getIsNull(&d.dpiData) would be safer, // but ODPI-C 3.1.4 just returns dpiData->isNull, so do the same // without calling CGO. - return d == nil || d.dpiData == nil || d.dpiData.isNull == 1 + return d == nil || d.dpiData.isNull == 1 } // SetNull sets the value of the data to be the null value. func (d *Data) SetNull() { if !d.IsNull() { - // Maybe C.dpiData_setNull(d.dpiData) would be safer, but as we don't use C.dpiData_getIsNull, + // Maybe C.dpiData_setNull(&d.dpiData) would be safer, but as we don't use C.dpiData_getIsNull, // and those functions (at least in ODPI-C 3.1.4) just operate on data->isNull directly, // don't use CGO if possible. d.dpiData.isNull = 1 @@ -59,7 +60,7 @@ func (d *Data) SetNull() { // GetBool returns the bool data. func (d *Data) GetBool() bool { - return !d.IsNull() && C.dpiData_getBool(d.dpiData) == 1 + return !d.IsNull() && C.dpiData_getBool(&d.dpiData) == 1 } // SetBool sets the data as bool. @@ -68,7 +69,7 @@ func (d *Data) SetBool(b bool) { if b { i = 1 } - C.dpiData_setBool(d.dpiData, i) + C.dpiData_setBool(&d.dpiData, i) } // GetBytes returns the []byte from the data. @@ -76,7 +77,7 @@ func (d *Data) GetBytes() []byte { if d.IsNull() { return nil } - b := C.dpiData_getBytes(d.dpiData) + b := C.dpiData_getBytes(&d.dpiData) if b.ptr == nil || b.length == 0 { return nil } @@ -89,7 +90,7 @@ func (d *Data) SetBytes(b []byte) { d.dpiData.isNull = 1 return } - C.dpiData_setBytes(d.dpiData, (*C.char)(unsafe.Pointer(&b[0])), C.uint32_t(len(b))) + C.dpiData_setBytes(&d.dpiData, (*C.char)(unsafe.Pointer(&b[0])), C.uint32_t(len(b))) } // GetFloat32 gets float32 from the data. @@ -97,12 +98,12 @@ func (d *Data) GetFloat32() float32 { if d.IsNull() { return 0 } - return float32(C.dpiData_getFloat(d.dpiData)) + return float32(C.dpiData_getFloat(&d.dpiData)) } // SetFloat32 sets the data as float32. func (d *Data) SetFloat32(f float32) { - C.dpiData_setFloat(d.dpiData, C.float(f)) + C.dpiData_setFloat(&d.dpiData, C.float(f)) } // GetFloat64 gets float64 from the data. @@ -111,12 +112,12 @@ func (d *Data) GetFloat64() float64 { if d.IsNull() { return 0 } - return float64(C.dpiData_getDouble(d.dpiData)) + return float64(C.dpiData_getDouble(&d.dpiData)) } // SetFloat64 sets the data as float64. func (d *Data) SetFloat64(f float64) { - C.dpiData_setDouble(d.dpiData, C.double(f)) + C.dpiData_setDouble(&d.dpiData, C.double(f)) } // GetInt64 gets int64 from the data. @@ -124,12 +125,12 @@ func (d *Data) GetInt64() int64 { if d.IsNull() { return 0 } - return int64(C.dpiData_getInt64(d.dpiData)) + return int64(C.dpiData_getInt64(&d.dpiData)) } // SetInt64 sets the data as int64. func (d *Data) SetInt64(i int64) { - C.dpiData_setInt64(d.dpiData, C.int64_t(i)) + C.dpiData_setInt64(&d.dpiData, C.int64_t(i)) } // GetIntervalDS gets duration as interval date-seconds from data. @@ -137,7 +138,7 @@ func (d *Data) GetIntervalDS() time.Duration { if d.IsNull() { return 0 } - ds := C.dpiData_getIntervalDS(d.dpiData) + ds := C.dpiData_getIntervalDS(&d.dpiData) return time.Duration(ds.days)*24*time.Hour + time.Duration(ds.hours)*time.Hour + time.Duration(ds.minutes)*time.Minute + @@ -147,7 +148,7 @@ func (d *Data) GetIntervalDS() time.Duration { // SetIntervalDS sets the duration as interval date-seconds to data. func (d *Data) SetIntervalDS(dur time.Duration) { - C.dpiData_setIntervalDS(d.dpiData, + C.dpiData_setIntervalDS(&d.dpiData, C.int32_t(int64(dur.Hours())/24), C.int32_t(int64(dur.Hours())%24), C.int32_t(dur.Minutes()), C.int32_t(dur.Seconds()), C.int32_t(dur.Nanoseconds()), @@ -159,13 +160,13 @@ func (d *Data) GetIntervalYM() IntervalYM { if d.IsNull() { return IntervalYM{} } - ym := C.dpiData_getIntervalYM(d.dpiData) + ym := C.dpiData_getIntervalYM(&d.dpiData) return IntervalYM{Years: int(ym.years), Months: int(ym.months)} } // SetIntervalYM sets IntervalYM to the data. func (d *Data) SetIntervalYM(ym IntervalYM) { - C.dpiData_setIntervalYM(d.dpiData, C.int32_t(ym.Years), C.int32_t(ym.Months)) + C.dpiData_setIntervalYM(&d.dpiData, C.int32_t(ym.Years), C.int32_t(ym.Months)) } // GetLob gets data as Lob. @@ -173,31 +174,33 @@ func (d *Data) GetLob() *Lob { if d.IsNull() { return nil } - return &Lob{Reader: &dpiLobReader{dpiLob: C.dpiData_getLOB(d.dpiData)}} + return &Lob{Reader: &dpiLobReader{dpiLob: C.dpiData_getLOB(&d.dpiData)}} } // SetLob sets Lob to the data. func (d *Data) SetLob(lob *DirectLob) { - C.dpiData_setLOB(d.dpiData, lob.dpiLob) + C.dpiData_setLOB(&d.dpiData, lob.dpiLob) } // GetObject gets Object from data. // // As with all Objects, you MUST call Close on it when not needed anymore! func (d *Data) GetObject() *Object { - if d == nil || d.dpiData == nil { + if d == nil { panic("null") } if d.IsNull() { return nil } - o := C.dpiData_getObject(d.dpiData) + o := C.dpiData_getObject(&d.dpiData) if o == nil { return nil } - if C.dpiObject_addRef(o) == C.DPI_FAILURE { - panic(d.ObjectType.getError()) + if !d.implicitObj { + if C.dpiObject_addRef(o) == C.DPI_FAILURE { + panic(d.ObjectType.getError()) + } } obj := &Object{dpiObject: o, ObjectType: d.ObjectType} obj.init() @@ -206,7 +209,7 @@ func (d *Data) GetObject() *Object { // SetObject sets Object to data. func (d *Data) SetObject(o *Object) { - C.dpiData_setObject(d.dpiData, o.dpiObject) + C.dpiData_setObject(&d.dpiData, o.dpiObject) } // GetStmt gets Stmt from data. @@ -214,12 +217,12 @@ func (d *Data) GetStmt() driver.Stmt { if d.IsNull() { return nil } - return &statement{dpiStmt: C.dpiData_getStmt(d.dpiData)} + return &statement{dpiStmt: C.dpiData_getStmt(&d.dpiData)} } // SetStmt sets Stmt to data. func (d *Data) SetStmt(s *statement) { - C.dpiData_setStmt(d.dpiData, s.dpiStmt) + C.dpiData_setStmt(&d.dpiData, s.dpiStmt) } // GetTime gets Time from data. @@ -227,7 +230,7 @@ func (d *Data) GetTime() time.Time { if d.IsNull() { return time.Time{} } - ts := C.dpiData_getTimestamp(d.dpiData) + ts := C.dpiData_getTimestamp(&d.dpiData) return time.Date( int(ts.year), time.Month(ts.month), int(ts.day), int(ts.hour), int(ts.minute), int(ts.second), int(ts.fsecond), @@ -239,7 +242,7 @@ func (d *Data) GetTime() time.Time { // SetTime sets Time to data. func (d *Data) SetTime(t time.Time) { _, z := t.Zone() - C.dpiData_setTimestamp(d.dpiData, + C.dpiData_setTimestamp(&d.dpiData, C.int16_t(t.Year()), C.uint8_t(t.Month()), C.uint8_t(t.Day()), C.uint8_t(t.Hour()), C.uint8_t(t.Minute()), C.uint8_t(t.Second()), C.uint32_t(t.Nanosecond()), C.int8_t(z/3600), C.int8_t((z%3600)/60), @@ -251,12 +254,12 @@ func (d *Data) GetUint64() uint64 { if d.IsNull() { return 0 } - return uint64(C.dpiData_getUint64(d.dpiData)) + return uint64(C.dpiData_getUint64(&d.dpiData)) } // SetUint64 sets data to uint64. func (d *Data) SetUint64(u uint64) { - C.dpiData_setUint64(d.dpiData, C.uint64_t(u)) + C.dpiData_setUint64(&d.dpiData, C.uint64_t(u)) } // IntervalYM holds Years and Months as interval. @@ -301,9 +304,6 @@ func (d *Data) Set(v interface{}) error { if v == nil { return errors.Errorf("%s: %w", "nil type", ErrNotSupported) } - if d.dpiData == nil { - d.dpiData = &C.dpiData{isNull: 1} - } switch x := v.(type) { case int32: d.NativeTypeNum = C.DPI_NATIVE_TYPE_INT64 @@ -373,14 +373,15 @@ func (c *conn) NewData(baseType interface{}, sliceLen, bufSize int) ([]*Data, er return nil, err } - _, dpiData, err := c.newVar(vi) + v, dpiData, err := c.newVar(vi) if err != nil { return nil, err } + defer C.dpiVar_release(v) data := make([]*Data, sliceLen) for i := 0; i < sliceLen; i++ { - data[i] = &Data{dpiData: &dpiData[i], NativeTypeNum: vi.NatTyp} + data[i] = &Data{dpiData: dpiData[i], NativeTypeNum: vi.NatTyp} } return data, nil @@ -461,10 +462,7 @@ func newVarInfo(baseType interface{}, sliceLen, bufSize int) (varInfo, error) { func (d *Data) reset() { d.NativeTypeNum = 0 d.ObjectType = ObjectType{} - if d.dpiData == nil { - d.dpiData = &C.dpiData{} - } else { - d.SetBytes(nil) - } + d.implicitObj = false + d.SetBytes(nil) d.dpiData.isNull = 1 } diff --git a/vendor/github.com/godror/godror/drv.go b/vendor/github.com/godror/godror/drv.go index 8ad65fd4c58..5699088871e 100644 --- a/vendor/github.com/godror/godror/drv.go +++ b/vendor/github.com/godror/godror/drv.go @@ -26,7 +26,8 @@ // poolSessionMaxLifetime=1h& \ // poolSessionTimeout=30s& \ // timezone=Local& \ -// newPassword= +// newPassword= \ +// onInit=ALTER+SESSION+SET+current_schema%3Dmy_schema // // These are the defaults. Many advocate that a static session pool (min=max, incr=0) // is better, with 1-10 sessions per CPU thread. @@ -57,7 +58,6 @@ import ( "fmt" "hash/fnv" "io" - "math" "net/url" "strconv" "strings" @@ -87,7 +87,9 @@ const ( DpiVersionNumber = C.DPI_VERSION_NUMBER // DriverName is set on the connection to be seen in the DB - DriverName = "github.com/godror/godror : " + Version + // + // It cannot be longer than 30 bytes ! + DriverName = "godror : " + Version // DefaultPoolMinSessions specifies the default value for minSessions for pool creation. DefaultPoolMinSessions = 1 @@ -108,238 +110,39 @@ const ( DefaultMaxLifeTime = 1 * time.Hour ) -// Number as string -type Number string - -var ( - // Int64 for converting to-from int64. - Int64 = intType{} - // Float64 for converting to-from float64. - Float64 = floatType{} - // Num for converting to-from Number (string) - Num = numType{} -) - -type intType struct{} - -func (intType) String() string { return "Int64" } -func (intType) ConvertValue(v interface{}) (driver.Value, error) { - if Log != nil { - Log("ConvertValue", "Int64", "value", v) - } - switch x := v.(type) { - case int8: - return int64(x), nil - case int16: - return int64(x), nil - case int32: - return int64(x), nil - case int64: - return x, nil - case uint16: - return int64(x), nil - case uint32: - return int64(x), nil - case uint64: - return int64(x), nil - case float32: - if _, f := math.Modf(float64(x)); f != 0 { - return int64(x), errors.Errorf("non-zero fractional part: %f", f) - } - return int64(x), nil - case float64: - if _, f := math.Modf(x); f != 0 { - return int64(x), errors.Errorf("non-zero fractional part: %f", f) - } - return int64(x), nil - case string: - if x == "" { - return 0, nil - } - return strconv.ParseInt(x, 10, 64) - case Number: - if x == "" { - return 0, nil - } - return strconv.ParseInt(string(x), 10, 64) - default: - return nil, errors.Errorf("unknown type %T", v) - } -} - -type floatType struct{} - -func (floatType) String() string { return "Float64" } -func (floatType) ConvertValue(v interface{}) (driver.Value, error) { - if Log != nil { - Log("ConvertValue", "Float64", "value", v) - } - switch x := v.(type) { - case int8: - return float64(x), nil - case int16: - return float64(x), nil - case int32: - return float64(x), nil - case uint16: - return float64(x), nil - case uint32: - return float64(x), nil - case int64: - return float64(x), nil - case uint64: - return float64(x), nil - case float32: - return float64(x), nil - case float64: - return x, nil - case string: - if x == "" { - return 0, nil - } - return strconv.ParseFloat(x, 64) - case Number: - if x == "" { - return 0, nil - } - return strconv.ParseFloat(string(x), 64) - default: - return nil, errors.Errorf("unknown type %T", v) - } -} - -type numType struct{} - -func (numType) String() string { return "Num" } -func (numType) ConvertValue(v interface{}) (driver.Value, error) { - if Log != nil { - Log("ConvertValue", "Num", "value", v) - } - switch x := v.(type) { - case string: - if x == "" { - return 0, nil - } - return x, nil - case Number: - if x == "" { - return 0, nil - } - return string(x), nil - case int8, int16, int32, int64, uint16, uint32, uint64: - return fmt.Sprintf("%d", x), nil - case float32, float64: - return fmt.Sprintf("%f", x), nil - default: - return nil, errors.Errorf("unknown type %T", v) - } -} -func (n Number) String() string { return string(n) } - -// Value returns the Number as driver.Value -func (n Number) Value() (driver.Value, error) { - return string(n), nil -} - -// Scan into the Number from a driver.Value. -func (n *Number) Scan(v interface{}) error { - if v == nil { - *n = "" - return nil - } - switch x := v.(type) { - case string: - *n = Number(x) - case Number: - *n = x - case int8, int16, int32, int64, uint16, uint32, uint64: - *n = Number(fmt.Sprintf("%d", x)) - case float32, float64: - *n = Number(fmt.Sprintf("%f", x)) - default: - return errors.Errorf("unknown type %T", v) - } - return nil -} - -// MarshalText marshals a Number to text. -func (n Number) MarshalText() ([]byte, error) { return []byte(n), nil } - -// UnmarshalText parses text into a Number. -func (n *Number) UnmarshalText(p []byte) error { - var dotNum int - for i, c := range p { - if !(c == '-' && i == 0 || '0' <= c && c <= '9') { - if c == '.' { - dotNum++ - if dotNum == 1 { - continue - } - } - return errors.Errorf("unknown char %c in %q", c, p) - } - } - *n = Number(p) - return nil -} - -// MarshalJSON marshals a Number into a JSON string. -func (n Number) MarshalJSON() ([]byte, error) { - b, err := n.MarshalText() - b2 := make([]byte, 1, 1+len(b)+1) - b2[0] = '"' - b2 = append(b2, b...) - b2 = append(b2, '"') - return b2, err -} - -// UnmarshalJSON parses a JSON string into the Number. -func (n *Number) UnmarshalJSON(p []byte) error { - *n = Number("") - if len(p) == 0 { - return nil - } - if len(p) > 2 && p[0] == '"' && p[len(p)-1] == '"' { - p = p[1 : len(p)-1] - } - return n.UnmarshalText(p) -} - // Log function. By default, it's nil, and thus logs nothing. // If you want to change this, change it to a github.com/go-kit/kit/log.Swapper.Log // or analog to be race-free. var Log func(...interface{}) error -var defaultDrv *drv +var defaultDrv = &drv{} func init() { - defaultDrv = newDrv() sql.Register("godror", defaultDrv) } -func newDrv() *drv { - return &drv{pools: make(map[string]*connPool)} -} - var _ = driver.Driver((*drv)(nil)) type drv struct { - clientVersion VersionInfo mu sync.Mutex dpiContext *C.dpiContext pools map[string]*connPool + clientVersion VersionInfo } type connPool struct { dpiPool *C.dpiPool - serverVersion VersionInfo timeZone *time.Location tzOffSecs int + serverVersion VersionInfo } func (d *drv) init() error { d.mu.Lock() defer d.mu.Unlock() + if d.pools == nil { + d.pools = make(map[string]*connPool) + } if d.dpiContext != nil { return nil } @@ -376,81 +179,38 @@ func (d *drv) ClientVersion() (VersionInfo, error) { return d.clientVersion, nil } +var cUTF8, cDriverName = C.CString("AL32UTF8"), C.CString(DriverName) + func (d *drv) openConn(P ConnectionParams) (*conn, error) { if err := d.init(); err != nil { return nil, err } - c := conn{drv: d, connParams: P, timeZone: time.Local} + P.Comb() + c := &conn{drv: d, connParams: P, timeZone: time.Local, Client: d.clientVersion} connString := P.String() - defer func() { - d.mu.Lock() - if Log != nil { - Log("pools", d.pools, "conn", P.String()) - } - d.mu.Unlock() - }() - - authMode := C.dpiAuthMode(C.DPI_MODE_AUTH_DEFAULT) - // OR all the modes together - for _, elt := range []struct { - Is bool - Mode C.dpiAuthMode - }{ - {P.IsSysDBA, C.DPI_MODE_AUTH_SYSDBA}, - {P.IsSysOper, C.DPI_MODE_AUTH_SYSOPER}, - {P.IsSysASM, C.DPI_MODE_AUTH_SYSASM}, - {P.IsPrelim, C.DPI_MODE_AUTH_PRELIM}, - } { - if elt.Is { - authMode |= elt.Mode - } - } - P.StandaloneConnection = P.StandaloneConnection || P.ConnClass == NoConnectionPoolingConnectionClass - if P.IsPrelim || P.StandaloneConnection { - // Prelim: the shared memory may not exist when Oracle is shut down. - P.ConnClass = "" + if Log != nil { + defer func() { + d.mu.Lock() + Log("pools", d.pools, "conn", P.String(), "drv", fmt.Sprintf("%p", d)) + d.mu.Unlock() + }() } - extAuth := C.int(b2i(P.Username == "" && P.Password == "")) - var connCreateParams C.dpiConnCreateParams - if C.dpiContext_initConnCreateParams(d.dpiContext, &connCreateParams) == C.DPI_FAILURE { - return nil, errors.Errorf("initConnCreateParams: %w", d.getError()) - } - connCreateParams.authMode = authMode - connCreateParams.externalAuth = extAuth - if P.ConnClass != "" { - cConnClass := C.CString(P.ConnClass) - defer C.free(unsafe.Pointer(cConnClass)) - connCreateParams.connectionClass = cConnClass - connCreateParams.connectionClassLength = C.uint32_t(len(P.ConnClass)) - } if !(P.IsSysDBA || P.IsSysOper || P.IsSysASM || P.IsPrelim || P.StandaloneConnection) { d.mu.Lock() dp := d.pools[connString] d.mu.Unlock() if dp != nil { //Proxy authenticated connections to database will be provided by methods with context - c.mu.Lock() - c.Client, c.Server = d.clientVersion, dp.serverVersion - c.timeZone, c.tzOffSecs = dp.timeZone, dp.tzOffSecs - c.mu.Unlock() - if err := c.acquireConn("", ""); err != nil { - return nil, err - } - err := c.init() - if err == nil { - c.mu.Lock() - dp.serverVersion = c.Server - dp.timeZone, dp.tzOffSecs = c.timeZone, c.tzOffSecs - c.mu.Unlock() - } - return &c, err + err := dp.acquireConn(c, P) + return c, err } } - var cUserName, cPassword, cNewPassword *C.char + extAuth := C.int(b2i(P.Username == "" && P.Password == "")) + var cUserName, cPassword, cNewPassword, cConnClass *C.char if !(P.Username == "" && P.Password == "") { cUserName, cPassword = C.CString(P.Username), C.CString(P.Password) } @@ -458,8 +218,6 @@ func (d *drv) openConn(P ConnectionParams) (*conn, error) { if P.SID != "" { cSid = C.CString(P.SID) } - cUTF8, cConnClass := C.CString("AL32UTF8"), C.CString(P.ConnClass) - cDriverName := C.CString(DriverName) defer func() { if cUserName != nil { C.free(unsafe.Pointer(cUserName)) @@ -471,9 +229,9 @@ func (d *drv) openConn(P ConnectionParams) (*conn, error) { if cSid != nil { C.free(unsafe.Pointer(cSid)) } - C.free(unsafe.Pointer(cUTF8)) - C.free(unsafe.Pointer(cConnClass)) - C.free(unsafe.Pointer(cDriverName)) + if cConnClass != nil { + C.free(unsafe.Pointer(cConnClass)) + } }() var commonCreateParams C.dpiCommonCreateParams if C.dpiContext_initCommonCreateParams(d.dpiContext, &commonCreateParams) == C.DPI_FAILURE { @@ -489,32 +247,9 @@ func (d *drv) openConn(P ConnectionParams) (*conn, error) { commonCreateParams.driverNameLength = C.uint32_t(len(DriverName)) if P.IsSysDBA || P.IsSysOper || P.IsSysASM || P.IsPrelim || P.StandaloneConnection { - if P.NewPassword != "" { - cNewPassword = C.CString(P.NewPassword) - connCreateParams.newPassword = cNewPassword - connCreateParams.newPasswordLength = C.uint32_t(len(P.NewPassword)) - } - dc := C.malloc(C.sizeof_void) - if Log != nil { - Log("C", "dpiConn_create", "params", P.String(), "common", commonCreateParams, "conn", connCreateParams) - } - if C.dpiConn_create( - d.dpiContext, - cUserName, C.uint32_t(len(P.Username)), - cPassword, C.uint32_t(len(P.Password)), - cSid, C.uint32_t(len(P.SID)), - &commonCreateParams, - &connCreateParams, - (**C.dpiConn)(unsafe.Pointer(&dc)), - ) == C.DPI_FAILURE { - C.free(unsafe.Pointer(dc)) - return nil, errors.Errorf("username=%q sid=%q params=%+v: %w", P.Username, P.SID, connCreateParams, d.getError()) - } - c.dpiConn = (*C.dpiConn)(dc) - c.currentUser = P.Username - c.newSession = true - err := c.init() - return &c, err + // no pool + c.connParams = P + return c, c.acquireConn(P.Username, P.Password, P.ConnClass) } var poolCreateParams C.dpiPoolCreateParams if C.dpiContext_initPoolCreateParams(d.dpiContext, &poolCreateParams) == C.DPI_FAILURE { @@ -566,24 +301,79 @@ func (d *drv) openConn(P ConnectionParams) (*conn, error) { return nil, errors.Errorf("params=%s extAuth=%v: %w", P.String(), extAuth, d.getError()) } C.dpiPool_setStmtCacheSize(dp, 40) + pool := &connPool{dpiPool: dp} d.mu.Lock() - d.pools[connString] = &connPool{dpiPool: dp} + d.pools[connString] = pool d.mu.Unlock() - return d.openConn(P) + return c, pool.acquireConn(c, P) } -func (c *conn) acquireConn(user, pass string) error { +func (dp *connPool) acquireConn(c *conn, P ConnectionParams) error { + P.Comb() + c.mu.Lock() + c.connParams = P + c.Client, c.Server = c.drv.clientVersion, dp.serverVersion + c.timeZone, c.tzOffSecs = dp.timeZone, dp.tzOffSecs + c.mu.Unlock() + var connCreateParams C.dpiConnCreateParams - if C.dpiContext_initConnCreateParams(c.dpiContext, &connCreateParams) == C.DPI_FAILURE { - return errors.Errorf("initConnCreateParams: %w", "", c.getError()) + if C.dpiContext_initConnCreateParams(c.drv.dpiContext, &connCreateParams) == C.DPI_FAILURE { + return errors.Errorf("initConnCreateParams: %w", c.drv.getError()) + } + if P.ConnClass != "" { + cConnClass := C.CString(P.ConnClass) + defer C.free(unsafe.Pointer(cConnClass)) + connCreateParams.connectionClass = cConnClass + connCreateParams.connectionClassLength = C.uint32_t(len(P.ConnClass)) } - dc := C.malloc(C.sizeof_void) - if Log != nil { - Log("C", "dpiPool_acquirePoolConnection", "conn", connCreateParams) + if C.dpiPool_acquireConnection( + dp.dpiPool, + nil, 0, nil, 0, + &connCreateParams, + (**C.dpiConn)(unsafe.Pointer(&dc)), + ) == C.DPI_FAILURE { + C.free(unsafe.Pointer(dc)) + return errors.Errorf("acquirePoolConnection(user=%q, params=%#v): %w", P.Username, connCreateParams, c.getError()) } - var cUserName, cPassword *C.char + + c.mu.Lock() + c.dpiConn = (*C.dpiConn)(dc) + c.currentUser = P.Username + c.newSession = connCreateParams.outNewSession == 1 + c.mu.Unlock() + err := c.init(P.OnInit) + if err == nil { + c.mu.Lock() + dp.serverVersion = c.Server + dp.timeZone, dp.tzOffSecs = c.timeZone, c.tzOffSecs + c.mu.Unlock() + } + + return err +} + +func (c *conn) acquireConn(user, pass, connClass string) error { + P := c.connParams + if !(P.IsSysDBA || P.IsSysOper || P.IsSysASM || P.IsPrelim || P.StandaloneConnection) { + c.drv.mu.Lock() + pool := c.drv.pools[P.String()] + if Log != nil { + Log("pools", c.drv.pools, "drv", fmt.Sprintf("%p", c.drv)) + } + c.drv.mu.Unlock() + if pool != nil { + P.Username, P.Password, P.ConnClass = user, pass, connClass + return pool.acquireConn(c, P) + } + } + + var connCreateParams C.dpiConnCreateParams + if C.dpiContext_initConnCreateParams(c.drv.dpiContext, &connCreateParams) == C.DPI_FAILURE { + return errors.Errorf("initConnCreateParams: %w", c.drv.getError()) + } + var cUserName, cPassword, cNewPassword, cConnClass, cSid *C.char defer func() { if cUserName != nil { C.free(unsafe.Pointer(cUserName)) @@ -591,6 +381,15 @@ func (c *conn) acquireConn(user, pass string) error { if cPassword != nil { C.free(unsafe.Pointer(cPassword)) } + if cNewPassword != nil { + C.free(unsafe.Pointer(cNewPassword)) + } + if cConnClass != nil { + C.free(unsafe.Pointer(cConnClass)) + } + if cSid != nil { + C.free(unsafe.Pointer(cSid)) + } }() if user != "" { cUserName = C.CString(user) @@ -598,52 +397,79 @@ func (c *conn) acquireConn(user, pass string) error { if pass != "" { cPassword = C.CString(pass) } + if connClass != "" { + cConnClass = C.CString(connClass) + connCreateParams.connectionClass = cConnClass + connCreateParams.connectionClassLength = C.uint32_t(len(connClass)) + } + var commonCreateParams C.dpiCommonCreateParams + if C.dpiContext_initCommonCreateParams(c.drv.dpiContext, &commonCreateParams) == C.DPI_FAILURE { + return errors.Errorf("initCommonCreateParams: %w", c.drv.getError()) + } + commonCreateParams.createMode = C.DPI_MODE_CREATE_DEFAULT | C.DPI_MODE_CREATE_THREADED + if P.EnableEvents { + commonCreateParams.createMode |= C.DPI_MODE_CREATE_EVENTS + } + commonCreateParams.encoding = cUTF8 + commonCreateParams.nencoding = cUTF8 + commonCreateParams.driverName = cDriverName + commonCreateParams.driverNameLength = C.uint32_t(len(DriverName)) - c.drv.mu.Lock() - pool := c.pools[c.connParams.String()] - c.drv.mu.Unlock() - if C.dpiPool_acquireConnection( - pool.dpiPool, - cUserName, C.uint32_t(len(user)), cPassword, C.uint32_t(len(pass)), + if P.SID != "" { + cSid = C.CString(P.SID) + } + connCreateParams.authMode = P.authMode() + extAuth := C.int(b2i(user == "" && pass == "")) + connCreateParams.externalAuth = extAuth + if P.NewPassword != "" { + cNewPassword = C.CString(P.NewPassword) + connCreateParams.newPassword = cNewPassword + connCreateParams.newPasswordLength = C.uint32_t(len(P.NewPassword)) + } + if Log != nil { + Log("C", "dpiConn_create", "params", P.String(), "common", commonCreateParams, "conn", connCreateParams) + } + dc := C.malloc(C.sizeof_void) + if C.dpiConn_create( + c.drv.dpiContext, + cUserName, C.uint32_t(len(user)), + cPassword, C.uint32_t(len(pass)), + cSid, C.uint32_t(len(P.SID)), + &commonCreateParams, &connCreateParams, (**C.dpiConn)(unsafe.Pointer(&dc)), ) == C.DPI_FAILURE { C.free(unsafe.Pointer(dc)) - return errors.Errorf("acquirePoolConnection: %w", c.getError()) + return errors.Errorf("username=%q sid=%q params=%+v: %w", user, P.SID, connCreateParams, c.drv.getError()) } - c.mu.Lock() c.dpiConn = (*C.dpiConn)(dc) c.currentUser = user - c.newSession = connCreateParams.outNewSession == 1 - c.Client, c.Server = c.drv.clientVersion, pool.serverVersion - c.timeZone, c.tzOffSecs = pool.timeZone, pool.tzOffSecs - c.mu.Unlock() - err := c.init() - if err == nil { - c.mu.Lock() - pool.serverVersion = c.Server - pool.timeZone, pool.tzOffSecs = c.timeZone, c.tzOffSecs - c.mu.Unlock() + c.newSession = true + P.Username, P.Password, P.ConnClass = user, pass, connClass + if P.NewPassword != "" { + P.Password, P.NewPassword = P.NewPassword, "" } - - return err + c.connParams = P + c.mu.Unlock() + return c.init(P.OnInit) } // ConnectionParams holds the params for a connection (pool). // You can use ConnectionParams{...}.StringWithPassword() // as a connection string in sql.Open. type ConnectionParams struct { + OnInit []string Username, Password, SID, ConnClass string // NewPassword is used iff StandaloneConnection is true! NewPassword string MinSessions, MaxSessions, PoolIncrement int WaitTimeout, MaxLifeTime, SessionTimeout time.Duration + Timezone *time.Location IsSysDBA, IsSysOper, IsSysASM, IsPrelim bool HeterogeneousPool bool StandaloneConnection bool EnableEvents bool - Timezone *time.Location } // String returns the string representation of ConnectionParams. @@ -713,6 +539,7 @@ func (P ConnectionParams) string(class, withPassword bool) string { q.Add("poolWaitTimeout", P.WaitTimeout.String()) q.Add("poolSessionMaxLifetime", P.MaxLifeTime.String()) q.Add("poolSessionTimeout", P.SessionTimeout.String()) + q["onInit"] = P.OnInit return (&url.URL{ Scheme: "oracle", User: url.UserPassword(P.Username, password), @@ -722,6 +549,15 @@ func (P ConnectionParams) string(class, withPassword bool) string { }).String() } +func (P *ConnectionParams) Comb() { + P.StandaloneConnection = P.StandaloneConnection || P.ConnClass == NoConnectionPoolingConnectionClass + if P.IsPrelim || P.StandaloneConnection { + // Prelim: the shared memory may not exist when Oracle is shut down. + P.ConnClass = "" + P.HeterogeneousPool = false + } +} + // ParseConnString parses the given connection string into a struct. func ParseConnString(connString string) (ConnectionParams, error) { P := ConnectionParams{ @@ -815,14 +651,6 @@ func ParseConnString(connString string) (ConnectionParams, error) { } } - P.StandaloneConnection = P.StandaloneConnection || P.ConnClass == NoConnectionPoolingConnectionClass - if P.IsPrelim { - P.ConnClass = "" - } - if P.StandaloneConnection { - P.NewPassword = q.Get("newPassword") - } - for _, task := range []struct { Dest *int Key string @@ -878,9 +706,40 @@ func ParseConnString(connString string) (ConnectionParams, error) { } else if P.PoolIncrement < 1 { P.PoolIncrement = 1 } + P.OnInit = q["onInit"] + + P.Comb() + if P.StandaloneConnection { + P.NewPassword = q.Get("newPassword") + } + return P, nil } +// SetSessionParamOnInit adds an "ALTER SESSION k=v" to the OnInit task list. +func (P *ConnectionParams) SetSessionParamOnInit(k, v string) { + P.OnInit = append(P.OnInit, fmt.Sprintf("ALTER SESSION SET %s = q'(%s)'", k, strings.Replace(v, "'", "''", -1))) +} + +func (P ConnectionParams) authMode() C.dpiAuthMode { + authMode := C.dpiAuthMode(C.DPI_MODE_AUTH_DEFAULT) + // OR all the modes together + for _, elt := range []struct { + Is bool + Mode C.dpiAuthMode + }{ + {P.IsSysDBA, C.DPI_MODE_AUTH_SYSDBA}, + {P.IsSysOper, C.DPI_MODE_AUTH_SYSOPER}, + {P.IsSysASM, C.DPI_MODE_AUTH_SYSASM}, + {P.IsPrelim, C.DPI_MODE_AUTH_PRELIM}, + } { + if elt.Is { + authMode |= elt.Mode + } + } + return authMode +} + // OraErr is an error holding the ORA-01234 code and the message. type OraErr struct { message string @@ -950,15 +809,15 @@ func b2i(b bool) uint8 { // VersionInfo holds version info returned by Oracle DB. type VersionInfo struct { ServerRelease string - Version, Release, Update, PortRelease, PortUpdate, Full int + Version, Release, Update, PortRelease, PortUpdate, Full uint8 } func (V *VersionInfo) set(v *C.dpiVersionInfo) { *V = VersionInfo{ - Version: int(v.versionNum), - Release: int(v.releaseNum), Update: int(v.updateNum), - PortRelease: int(v.portReleaseNum), PortUpdate: int(v.portUpdateNum), - Full: int(v.fullVersionNum), + Version: uint8(v.versionNum), + Release: uint8(v.releaseNum), Update: uint8(v.updateNum), + PortRelease: uint8(v.portReleaseNum), PortUpdate: uint8(v.portUpdateNum), + Full: uint8(v.fullVersionNum), } } func (V VersionInfo) String() string { @@ -1012,12 +871,13 @@ func ContextWithLog(ctx context.Context, logF func(...interface{}) error) contex return context.WithValue(ctx, logCtxKey, logF) } +var _ = driver.DriverContext((*drv)(nil)) var _ = driver.Connector((*connector)(nil)) type connector struct { - ConnectionParams - *drv + drv *drv onInit func(driver.Conn) error + ConnectionParams } // OpenConnector must parse the name in the same format that Driver.Open @@ -1062,9 +922,9 @@ func (c connector) Driver() driver.Driver { return c.drv } // NewConnector returns a driver.Connector to be used with sql.OpenDB, // which calls the given onInit if the connection is new. // -// For an example, see NewSessionIniter. -func NewConnector(name string, onInit func(driver.Conn) error) (driver.Connector, error) { - cxr, err := defaultDrv.OpenConnector(name) +// For an onInit example, see NewSessionIniter. +func (d *drv) NewConnector(name string, onInit func(driver.Conn) error) (driver.Connector, error) { + cxr, err := d.OpenConnector(name) if err != nil { return nil, err } @@ -1073,6 +933,15 @@ func NewConnector(name string, onInit func(driver.Conn) error) (driver.Connector return cx, err } +// NewConnector returns a driver.Connector to be used with sql.OpenDB, +// (for the default Driver registered with godror) +// which calls the given onInit if the connection is new. +// +// For an onInit example, see NewSessionIniter. +func NewConnector(name string, onInit func(driver.Conn) error) (driver.Connector, error) { + return defaultDrv.NewConnector(name, onInit) +} + // NewSessionIniter returns a function suitable for use in NewConnector as onInit, // which calls "ALTER SESSION SET =''" for each element of the given map. func NewSessionIniter(m map[string]string) func(driver.Conn) error { diff --git a/vendor/github.com/godror/godror/go.mod b/vendor/github.com/godror/godror/go.mod index d7c794d6705..86f145abad2 100644 --- a/vendor/github.com/godror/godror/go.mod +++ b/vendor/github.com/godror/godror/go.mod @@ -4,7 +4,7 @@ go 1.12 require ( github.com/go-kit/kit v0.9.0 - github.com/go-logfmt/logfmt v0.4.0 // indirect + github.com/go-logfmt/logfmt v0.4.0 github.com/go-stack/stack v1.8.0 // indirect github.com/google/go-cmp v0.3.1 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e diff --git a/vendor/github.com/godror/godror/obj.go b/vendor/github.com/godror/godror/obj.go index a5d35505420..17d03d732fa 100644 --- a/vendor/github.com/godror/godror/obj.go +++ b/vendor/github.com/godror/godror/obj.go @@ -15,6 +15,7 @@ import ( "fmt" "reflect" "strings" + "sync" "unsafe" errors "golang.org/x/xerrors" @@ -24,9 +25,8 @@ var _ = fmt.Printf // Object represents a dpiObject. type Object struct { - ObjectType - scratch Data dpiObject *C.dpiObject + ObjectType } func (O *Object) getError() error { return O.conn.getError() } @@ -45,20 +45,18 @@ func (O *Object) GetAttribute(data *Data, name string) error { } data.reset() - if data.dpiData == nil { - data.dpiData = &C.dpiData{isNull: 0} - } data.NativeTypeNum = attr.NativeTypeNum data.ObjectType = attr.ObjectType + data.implicitObj = true // the maximum length of that buffer must be supplied // in the value.asBytes.length attribute before calling this function. if attr.NativeTypeNum == C.DPI_NATIVE_TYPE_BYTES && attr.OracleTypeNum == C.DPI_ORACLE_TYPE_NUMBER { var a [39]byte - C.dpiData_setBytes(data.dpiData, (*C.char)(unsafe.Pointer(&a[0])), C.uint32_t(len(a))) + C.dpiData_setBytes(&data.dpiData, (*C.char)(unsafe.Pointer(&a[0])), C.uint32_t(len(a))) } //fmt.Printf("getAttributeValue(%p, %p, %d, %+v)\n", O.dpiObject, attr.dpiObjectAttr, data.NativeTypeNum, data.dpiData) - if C.dpiObject_getAttributeValue(O.dpiObject, attr.dpiObjectAttr, data.NativeTypeNum, data.dpiData) == C.DPI_FAILURE { + if C.dpiObject_getAttributeValue(O.dpiObject, attr.dpiObjectAttr, data.NativeTypeNum, &data.dpiData) == C.DPI_FAILURE { return errors.Errorf("getAttributeValue(%q, obj=%+v, attr=%+v, typ=%d): %w", name, O, attr.dpiObjectAttr, data.NativeTypeNum, O.getError()) } //fmt.Printf("getAttributeValue(%p, %q=%p, %d, %+v)\n", O.dpiObject, attr.Name, attr.dpiObjectAttr, data.NativeTypeNum, data.dpiData) @@ -75,7 +73,7 @@ func (O *Object) SetAttribute(name string, data *Data) error { data.NativeTypeNum = attr.NativeTypeNum data.ObjectType = attr.ObjectType } - if C.dpiObject_setAttributeValue(O.dpiObject, attr.dpiObjectAttr, data.NativeTypeNum, data.dpiData) == C.DPI_FAILURE { + if C.dpiObject_setAttributeValue(O.dpiObject, attr.dpiObjectAttr, data.NativeTypeNum, &data.dpiData) == C.DPI_FAILURE { return O.getError() } return nil @@ -86,10 +84,12 @@ func (O *Object) Set(name string, v interface{}) error { if data, ok := v.(*Data); ok { return O.SetAttribute(name, data) } - if err := O.scratch.Set(v); err != nil { + d := scratch.Get() + defer scratch.Put(d) + if err := d.Set(v); err != nil { return err } - return O.SetAttribute(name, &O.scratch) + return O.SetAttribute(name, d) } // ResetAttributes prepare all attributes for use the object as IN parameter @@ -101,9 +101,9 @@ func (O *Object) ResetAttributes() error { data.ObjectType = attr.ObjectType if attr.NativeTypeNum == C.DPI_NATIVE_TYPE_BYTES && attr.OracleTypeNum == C.DPI_ORACLE_TYPE_NUMBER { a := make([]byte, attr.Precision) - C.dpiData_setBytes(data.dpiData, (*C.char)(unsafe.Pointer(&a[0])), C.uint32_t(attr.Precision)) + C.dpiData_setBytes(&data.dpiData, (*C.char)(unsafe.Pointer(&a[0])), C.uint32_t(attr.Precision)) } - if C.dpiObject_setAttributeValue(O.dpiObject, attr.dpiObjectAttr, data.NativeTypeNum, data.dpiData) == C.DPI_FAILURE { + if C.dpiObject_setAttributeValue(O.dpiObject, attr.dpiObjectAttr, data.NativeTypeNum, &data.dpiData) == C.DPI_FAILURE { return O.getError() } } @@ -113,14 +113,16 @@ func (O *Object) ResetAttributes() error { // Get scans the named attribute into dest, and returns it. func (O *Object) Get(name string) (interface{}, error) { - if err := O.GetAttribute(&O.scratch, name); err != nil { + d := scratch.Get() + defer scratch.Put(d) + if err := O.GetAttribute(d, name); err != nil { return nil, err } - isObject := O.scratch.IsObject() + isObject := d.IsObject() if isObject { - O.scratch.ObjectType = O.Attributes[name].ObjectType + d.ObjectType = O.Attributes[name].ObjectType } - v := O.scratch.Get() + v := d.Get() if !isObject { return v, nil } @@ -138,11 +140,11 @@ func (O *Object) ObjectRef() *Object { // Collection returns &ObjectCollection{Object: O} iff the Object is a collection. // Otherwise it returns nil. -func (O *Object) Collection() *ObjectCollection { +func (O *Object) Collection() ObjectCollection { if O.ObjectType.CollectionOf == nil { - return nil + return ObjectCollection{} } - return &ObjectCollection{Object: O} + return ObjectCollection{Object: O} } // Close releases a reference to the object. @@ -171,20 +173,22 @@ var ErrNotCollection = errors.New("not collection") var ErrNotExist = errors.New("not exist") // AsSlice retrieves the collection into a slice. -func (O *ObjectCollection) AsSlice(dest interface{}) (interface{}, error) { +func (O ObjectCollection) AsSlice(dest interface{}) (interface{}, error) { var dr reflect.Value needsInit := dest == nil if !needsInit { dr = reflect.ValueOf(dest) } + d := scratch.Get() + defer scratch.Put(d) for i, err := O.First(); err == nil; i, err = O.Next(i) { if O.CollectionOf.NativeTypeNum == C.DPI_NATIVE_TYPE_OBJECT { - O.scratch.ObjectType = *O.CollectionOf + d.ObjectType = *O.CollectionOf } - if err = O.GetItem(&O.scratch, i); err != nil { + if err = O.GetItem(d, i); err != nil { return dest, err } - vr := reflect.ValueOf(O.scratch.Get()) + vr := reflect.ValueOf(d.Get()) if needsInit { needsInit = false length, lengthErr := O.Len() @@ -199,37 +203,38 @@ func (O *ObjectCollection) AsSlice(dest interface{}) (interface{}, error) { } // AppendData to the collection. -func (O *ObjectCollection) AppendData(data *Data) error { - if C.dpiObject_appendElement(O.dpiObject, data.NativeTypeNum, data.dpiData) == C.DPI_FAILURE { +func (O ObjectCollection) AppendData(data *Data) error { + if C.dpiObject_appendElement(O.dpiObject, data.NativeTypeNum, &data.dpiData) == C.DPI_FAILURE { return errors.Errorf("append(%d): %w", data.NativeTypeNum, O.getError()) } return nil } // Append v to the collection. -func (O *ObjectCollection) Append(v interface{}) error { +func (O ObjectCollection) Append(v interface{}) error { if data, ok := v.(*Data); ok { return O.AppendData(data) } - if err := O.scratch.Set(v); err != nil { + d := scratch.Get() + defer scratch.Put(d) + if err := d.Set(v); err != nil { return err } - return O.AppendData(&O.scratch) + return O.AppendData(d) } // AppendObject adds an Object to the collection. -func (O *ObjectCollection) AppendObject(obj *Object) error { - O.scratch = Data{ - ObjectType: obj.ObjectType, - NativeTypeNum: C.DPI_NATIVE_TYPE_OBJECT, - dpiData: &C.dpiData{isNull: 1}, - } - O.scratch.SetObject(obj) - return O.Append(&O.scratch) +func (O ObjectCollection) AppendObject(obj *Object) error { + d := scratch.Get() + defer scratch.Put(d) + d.ObjectType = obj.ObjectType + d.NativeTypeNum = C.DPI_NATIVE_TYPE_OBJECT + d.SetObject(obj) + return O.Append(d) } // Delete i-th element of the collection. -func (O *ObjectCollection) Delete(i int) error { +func (O ObjectCollection) Delete(i int) error { if C.dpiObject_deleteElementByIndex(O.dpiObject, C.int32_t(i)) == C.DPI_FAILURE { return errors.Errorf("delete(%d): %w", i, O.getError()) } @@ -237,7 +242,7 @@ func (O *ObjectCollection) Delete(i int) error { } // GetItem gets the i-th element of the collection into data. -func (O *ObjectCollection) GetItem(data *Data, i int) error { +func (O ObjectCollection) GetItem(data *Data, i int) error { if data == nil { panic("data cannot be nil") } @@ -252,40 +257,43 @@ func (O *ObjectCollection) GetItem(data *Data, i int) error { data.reset() data.NativeTypeNum = O.CollectionOf.NativeTypeNum data.ObjectType = *O.CollectionOf - if C.dpiObject_getElementValueByIndex(O.dpiObject, idx, data.NativeTypeNum, data.dpiData) == C.DPI_FAILURE { + data.implicitObj = true + if C.dpiObject_getElementValueByIndex(O.dpiObject, idx, data.NativeTypeNum, &data.dpiData) == C.DPI_FAILURE { return errors.Errorf("get(%d[%d]): %w", idx, data.NativeTypeNum, O.getError()) } return nil } // Get the i-th element of the collection. -func (O *ObjectCollection) Get(i int) (interface{}, error) { +func (O ObjectCollection) Get(i int) (interface{}, error) { var data Data err := O.GetItem(&data, i) return data.Get(), err } // SetItem sets the i-th element of the collection with data. -func (O *ObjectCollection) SetItem(i int, data *Data) error { - if C.dpiObject_setElementValueByIndex(O.dpiObject, C.int32_t(i), data.NativeTypeNum, data.dpiData) == C.DPI_FAILURE { +func (O ObjectCollection) SetItem(i int, data *Data) error { + if C.dpiObject_setElementValueByIndex(O.dpiObject, C.int32_t(i), data.NativeTypeNum, &data.dpiData) == C.DPI_FAILURE { return errors.Errorf("set(%d[%d]): %w", i, data.NativeTypeNum, O.getError()) } return nil } // Set the i-th element of the collection with value. -func (O *ObjectCollection) Set(i int, v interface{}) error { +func (O ObjectCollection) Set(i int, v interface{}) error { if data, ok := v.(*Data); ok { return O.SetItem(i, data) } - if err := O.scratch.Set(v); err != nil { + d := scratch.Get() + defer scratch.Put(d) + if err := d.Set(v); err != nil { return err } - return O.SetItem(i, &O.scratch) + return O.SetItem(i, d) } // First returns the first element's index of the collection. -func (O *ObjectCollection) First() (int, error) { +func (O ObjectCollection) First() (int, error) { var exists C.int var idx C.int32_t if C.dpiObject_getFirstIndex(O.dpiObject, &idx, &exists) == C.DPI_FAILURE { @@ -298,7 +306,7 @@ func (O *ObjectCollection) First() (int, error) { } // Last returns the index of the last element. -func (O *ObjectCollection) Last() (int, error) { +func (O ObjectCollection) Last() (int, error) { var exists C.int var idx C.int32_t if C.dpiObject_getLastIndex(O.dpiObject, &idx, &exists) == C.DPI_FAILURE { @@ -311,7 +319,7 @@ func (O *ObjectCollection) Last() (int, error) { } // Next returns the succeeding index of i. -func (O *ObjectCollection) Next(i int) (int, error) { +func (O ObjectCollection) Next(i int) (int, error) { var exists C.int var idx C.int32_t if C.dpiObject_getNextIndex(O.dpiObject, C.int32_t(i), &idx, &exists) == C.DPI_FAILURE { @@ -324,7 +332,7 @@ func (O *ObjectCollection) Next(i int) (int, error) { } // Len returns the length of the collection. -func (O *ObjectCollection) Len() (int, error) { +func (O ObjectCollection) Len() (int, error) { var size C.int32_t if C.dpiObject_getSize(O.dpiObject, &size) == C.DPI_FAILURE { return 0, errors.Errorf("len: %w", O.getError()) @@ -333,7 +341,7 @@ func (O *ObjectCollection) Len() (int, error) { } // Trim the collection to n. -func (O *ObjectCollection) Trim(n int) error { +func (O ObjectCollection) Trim(n int) error { if C.dpiObject_trim(O.dpiObject, C.uint32_t(n)) == C.DPI_FAILURE { return O.getError() } @@ -342,13 +350,14 @@ func (O *ObjectCollection) Trim(n int) error { // ObjectType holds type info of an Object. type ObjectType struct { - dpiObjectType *C.dpiObjectType + Schema, Name string + Attributes map[string]ObjectAttribute + conn *conn + dpiObjectType *C.dpiObjectType - Schema, Name string DBSize, ClientSizeInBytes, CharSize int CollectionOf *ObjectType - Attributes map[string]ObjectAttribute OracleTypeNum C.dpiOracleTypeNum NativeTypeNum C.dpiNativeTypeNum Precision int16 @@ -397,6 +406,7 @@ func (c *conn) GetObjectType(name string) (ObjectType, error) { err := t.init() if err == nil { c.objTypes[name] = t + c.objTypes[t.FullName()] = t } return t, err } @@ -417,25 +427,32 @@ func (t ObjectType) NewObject() (*Object, error) { // NewCollection returns a new Collection object with ObjectType type. // If the ObjectType is not a Collection, it returns ErrNotCollection error. -func (t ObjectType) NewCollection() (*ObjectCollection, error) { +func (t ObjectType) NewCollection() (ObjectCollection, error) { if t.CollectionOf == nil { - return nil, ErrNotCollection + return ObjectCollection{}, ErrNotCollection } O, err := t.NewObject() if err != nil { - return nil, err + return ObjectCollection{}, err } - return &ObjectCollection{Object: O}, nil + return ObjectCollection{Object: O}, nil } // Close releases a reference to the object type. -func (t *ObjectType) close() error { +func (t *ObjectType) close(doNotReuse bool) error { if t == nil { return nil } attributes, d := t.Attributes, t.dpiObjectType t.Attributes, t.dpiObjectType = nil, nil + if t.CollectionOf != nil { + err := t.CollectionOf.close(false) + if err != nil { + return err + } + } + for _, attr := range attributes { err := attr.Close() if err != nil { @@ -443,7 +460,7 @@ func (t *ObjectType) close() error { } } - if d == nil { + if d == nil || !doNotReuse { return nil } @@ -500,9 +517,12 @@ func (t *ObjectType) init() error { t.CollectionOf.Name = t.Name } } + if ot, ok := t.conn.objTypes[t.FullName()]; ok { + t.Attributes = ot.Attributes + return nil + } if numAttributes == 0 { t.Attributes = map[string]ObjectAttribute{} - t.conn.objTypes[t.FullName()] = *t return nil } t.Attributes = make(map[string]ObjectAttribute, numAttributes) @@ -534,7 +554,6 @@ func (t *ObjectType) init() error { //fmt.Printf("%d=%q. typ=%+v sub=%+v\n", i, objAttr.Name, typ, sub) t.Attributes[objAttr.Name] = objAttr } - t.conn.objTypes[t.FullName()] = *t return nil } @@ -581,6 +600,12 @@ func (A ObjectAttribute) Close() error { if C.dpiObjectAttr_release(attr) == C.DPI_FAILURE { return A.getError() } + if A.ObjectType.dpiObjectType != nil { + err := A.ObjectType.close(false) + if err != nil { + return err + } + } return nil } @@ -592,3 +617,10 @@ func GetObjectType(ctx context.Context, ex Execer, typeName string) (ObjectType, } return c.GetObjectType(typeName) } + +var scratch = &dataPool{Pool: sync.Pool{New: func() interface{} { return &Data{} }}} + +type dataPool struct{ sync.Pool } + +func (dp *dataPool) Get() *Data { return dp.Pool.Get().(*Data) } +func (dp *dataPool) Put(d *Data) { d.reset(); dp.Pool.Put(d) } diff --git a/vendor/github.com/godror/godror/orahlp.go b/vendor/github.com/godror/godror/orahlp.go index 94e1f74828d..b9b5068f51f 100644 --- a/vendor/github.com/godror/godror/orahlp.go +++ b/vendor/github.com/godror/godror/orahlp.go @@ -13,12 +13,211 @@ import ( "database/sql/driver" "fmt" "io" + "math" + "strconv" "sync" "time" errors "golang.org/x/xerrors" ) +// Number as string +type Number string + +var ( + // Int64 for converting to-from int64. + Int64 = intType{} + // Float64 for converting to-from float64. + Float64 = floatType{} + // Num for converting to-from Number (string) + Num = numType{} +) + +type intType struct{} + +func (intType) String() string { return "Int64" } +func (intType) ConvertValue(v interface{}) (driver.Value, error) { + if Log != nil { + Log("ConvertValue", "Int64", "value", v) + } + switch x := v.(type) { + case int8: + return int64(x), nil + case int16: + return int64(x), nil + case int32: + return int64(x), nil + case int64: + return x, nil + case uint16: + return int64(x), nil + case uint32: + return int64(x), nil + case uint64: + return int64(x), nil + case float32: + if _, f := math.Modf(float64(x)); f != 0 { + return int64(x), errors.Errorf("non-zero fractional part: %f", f) + } + return int64(x), nil + case float64: + if _, f := math.Modf(x); f != 0 { + return int64(x), errors.Errorf("non-zero fractional part: %f", f) + } + return int64(x), nil + case string: + if x == "" { + return 0, nil + } + return strconv.ParseInt(x, 10, 64) + case Number: + if x == "" { + return 0, nil + } + return strconv.ParseInt(string(x), 10, 64) + default: + return nil, errors.Errorf("unknown type %T", v) + } +} + +type floatType struct{} + +func (floatType) String() string { return "Float64" } +func (floatType) ConvertValue(v interface{}) (driver.Value, error) { + if Log != nil { + Log("ConvertValue", "Float64", "value", v) + } + switch x := v.(type) { + case int8: + return float64(x), nil + case int16: + return float64(x), nil + case int32: + return float64(x), nil + case uint16: + return float64(x), nil + case uint32: + return float64(x), nil + case int64: + return float64(x), nil + case uint64: + return float64(x), nil + case float32: + return float64(x), nil + case float64: + return x, nil + case string: + if x == "" { + return 0, nil + } + return strconv.ParseFloat(x, 64) + case Number: + if x == "" { + return 0, nil + } + return strconv.ParseFloat(string(x), 64) + default: + return nil, errors.Errorf("unknown type %T", v) + } +} + +type numType struct{} + +func (numType) String() string { return "Num" } +func (numType) ConvertValue(v interface{}) (driver.Value, error) { + if Log != nil { + Log("ConvertValue", "Num", "value", v) + } + switch x := v.(type) { + case string: + if x == "" { + return 0, nil + } + return x, nil + case Number: + if x == "" { + return 0, nil + } + return string(x), nil + case int8, int16, int32, int64, uint16, uint32, uint64: + return fmt.Sprintf("%d", x), nil + case float32, float64: + return fmt.Sprintf("%f", x), nil + default: + return nil, errors.Errorf("unknown type %T", v) + } +} +func (n Number) String() string { return string(n) } + +// Value returns the Number as driver.Value +func (n Number) Value() (driver.Value, error) { + return string(n), nil +} + +// Scan into the Number from a driver.Value. +func (n *Number) Scan(v interface{}) error { + if v == nil { + *n = "" + return nil + } + switch x := v.(type) { + case string: + *n = Number(x) + case Number: + *n = x + case int8, int16, int32, int64, uint16, uint32, uint64: + *n = Number(fmt.Sprintf("%d", x)) + case float32, float64: + *n = Number(fmt.Sprintf("%f", x)) + default: + return errors.Errorf("unknown type %T", v) + } + return nil +} + +// MarshalText marshals a Number to text. +func (n Number) MarshalText() ([]byte, error) { return []byte(n), nil } + +// UnmarshalText parses text into a Number. +func (n *Number) UnmarshalText(p []byte) error { + var dotNum int + for i, c := range p { + if !(c == '-' && i == 0 || '0' <= c && c <= '9') { + if c == '.' { + dotNum++ + if dotNum == 1 { + continue + } + } + return errors.Errorf("unknown char %c in %q", c, p) + } + } + *n = Number(p) + return nil +} + +// MarshalJSON marshals a Number into a JSON string. +func (n Number) MarshalJSON() ([]byte, error) { + b, err := n.MarshalText() + b2 := make([]byte, 1, 1+len(b)+1) + b2[0] = '"' + b2 = append(b2, b...) + b2 = append(b2, '"') + return b2, err +} + +// UnmarshalJSON parses a JSON string into the Number. +func (n *Number) UnmarshalJSON(p []byte) error { + *n = Number("") + if len(p) == 0 { + return nil + } + if len(p) > 2 && p[0] == '"' && p[len(p)-1] == '"' { + p = p[1 : len(p)-1] + } + return n.UnmarshalText(p) +} + // QueryColumn is the described column. type QueryColumn struct { Name string diff --git a/vendor/github.com/godror/godror/queue.go b/vendor/github.com/godror/godror/queue.go index 01f081452af..70b4d018377 100644 --- a/vendor/github.com/godror/godror/queue.go +++ b/vendor/github.com/godror/godror/queue.go @@ -40,13 +40,13 @@ var DefaultDeqOptions = DeqOptions{ // Queue represents an Oracle Advanced Queue. type Queue struct { - conn *conn - dpiQueue *C.dpiQueue PayloadObjectType ObjectType + props []*C.dpiMsgProps name string + conn *conn + dpiQueue *C.dpiQueue - mu sync.Mutex - props []*C.dpiMsgProps + mu sync.Mutex } // NewQueue creates a new Queue. @@ -211,15 +211,15 @@ func (Q *Queue) Enqueue(messages []Message) error { // Message is a message - either received or being sent. type Message struct { - DeliveryMode DeliveryMode - Enqueued time.Time - Delay, Expiration int32 - Priority, NumAttempts int32 Correlation, ExceptionQ string + Enqueued time.Time MsgID, OriginalMsgID [16]byte - State MessageState Raw []byte + Delay, Expiration int32 + Priority, NumAttempts int32 Object *Object + DeliveryMode DeliveryMode + State MessageState } func (M *Message) toOra(d *drv, props *C.dpiMsgProps) error { @@ -334,7 +334,7 @@ func (M *Message) fromOra(c *conn, props *C.dpiMsgProps, objType *ObjectType) er if n > MsgIDLength { n = MsgIDLength } - copy(M.MsgID[:], (*((*[1 << 30]byte)(unsafe.Pointer(&value))))[:n:n]) + copy(M.MsgID[:], C.GoBytes(unsafe.Pointer(value), n)) } M.OriginalMsgID = zeroMsgID @@ -343,7 +343,7 @@ func (M *Message) fromOra(c *conn, props *C.dpiMsgProps, objType *ObjectType) er if n > MsgIDLength { n = MsgIDLength } - copy(M.OriginalMsgID[:], (*((*[1 << 30]byte)(unsafe.Pointer(&value))))[:n:n]) + copy(M.OriginalMsgID[:], C.GoBytes(unsafe.Pointer(value), n)) } M.Priority = 0 @@ -362,7 +362,7 @@ func (M *Message) fromOra(c *conn, props *C.dpiMsgProps, objType *ObjectType) er var obj *C.dpiObject if OK(C.dpiMsgProps_getPayload(props, &obj, &value, &length), "getPayload") { if obj == nil { - M.Raw = append(make([]byte, 0, length), ((*[1 << 30]byte)(unsafe.Pointer(value)))[:int(length):int(length)]...) + M.Raw = C.GoBytes(unsafe.Pointer(value), C.int(length)) } else { if C.dpiObject_addRef(obj) == C.DPI_FAILURE { return objType.getError() diff --git a/vendor/github.com/godror/godror/stmt.go b/vendor/github.com/godror/godror/stmt.go index 0e0775b363d..3a2f6d291a1 100644 --- a/vendor/github.com/godror/godror/stmt.go +++ b/vendor/github.com/godror/godror/stmt.go @@ -285,8 +285,8 @@ func (st *statement) ExecContext(ctx context.Context, args []driver.NamedValue) } st.isReturning = false - st.conn.RLock() - defer st.conn.RUnlock() + st.conn.mu.RLock() + defer st.conn.mu.RUnlock() // bind variables if err = st.bindVars(args, Log); err != nil { @@ -304,6 +304,7 @@ func (st *statement) ExecContext(ctx context.Context, args []driver.NamedValue) // execute go func() { defer close(done) + var err error Loop: for i := 0; i < 3; i++ { if err = ctx.Err(); err != nil { @@ -339,7 +340,7 @@ func (st *statement) ExecContext(ctx context.Context, args []driver.NamedValue) err = errors.Errorf("getInfo: %w", st.getError()) } st.isReturning = info.isReturning != 0 - return + break } var cdr interface{ Code() int } if !errors.As(err, &cdr) { @@ -379,7 +380,9 @@ func (st *statement) ExecContext(ctx context.Context, args []driver.NamedValue) Log("msg", "BREAK statement") } _ = st.Break() - st.close(false) + // 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 } @@ -465,8 +468,8 @@ func (st *statement) QueryContext(ctx context.Context, args []driver.NamedValue) st.Lock() defer st.Unlock() st.isReturning = false - st.conn.RLock() - defer st.conn.RUnlock() + st.conn.mu.RLock() + defer st.conn.mu.RUnlock() switch st.query { case getConnection: @@ -539,7 +542,9 @@ func (st *statement) QueryContext(ctx context.Context, args []driver.NamedValue) Log("msg", "BREAK query") } _ = st.Break() - st.close(false) + // 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 } @@ -1920,13 +1925,13 @@ func (c *conn) dataGetObject(v interface{}, data []C.dpiData) error { case *Object: d := Data{ ObjectType: out.ObjectType, - dpiData: &data[0], + dpiData: data[0], } *out = *d.GetObject() case ObjectScanner: d := Data{ ObjectType: out.ObjectRef().ObjectType, - dpiData: &data[0], + dpiData: data[0], } return out.Scan(d.GetObject()) default: diff --git a/vendor/github.com/godror/godror/subscr.go b/vendor/github.com/godror/godror/subscr.go index 07355e591c7..872f7ee6969 100644 --- a/vendor/github.com/godror/godror/subscr.go +++ b/vendor/github.com/godror/godror/subscr.go @@ -18,11 +18,19 @@ import "C" import ( "log" "strings" + "sync" "unsafe" errors "golang.org/x/xerrors" ) +// Cannot pass *Subscription to C, so pass an uint64 that points to this map entry +var ( + subscriptionsMu sync.Mutex + subscriptions = make(map[uint64]*Subscription) + subscriptionsID uint64 +) + // CallbackSubscr is the callback for C code on subscription event. //export CallbackSubscr func CallbackSubscr(ctx unsafe.Pointer, message *C.dpiSubscrMessage) { @@ -30,7 +38,9 @@ func CallbackSubscr(ctx unsafe.Pointer, message *C.dpiSubscrMessage) { if ctx == nil { return } - subscr := (*Subscription)(ctx) + subscriptionsMu.Lock() + subscr := subscriptions[*((*uint64)(ctx))] + subscriptionsMu.Unlock() getRows := func(rws *C.dpiSubscrMessageRow, rwsNum C.uint32_t) []RowEvent { if rwsNum == 0 { @@ -124,6 +134,7 @@ type Subscription struct { conn *conn dpiSubscr *C.dpiSubscr callback func(Event) + ID uint64 } func (s *Subscription) getError() error { return s.conn.getError() } @@ -140,7 +151,7 @@ func (c *conn) NewSubscription(name string, cb func(Event)) (*Subscription, erro subscr := Subscription{conn: c, callback: cb} params := (*C.dpiSubscrCreateParams)(C.malloc(C.sizeof_dpiSubscrCreateParams)) //defer func() { C.free(unsafe.Pointer(params)) }() - C.dpiContext_initSubscrCreateParams(c.dpiContext, params) + C.dpiContext_initSubscrCreateParams(c.drv.dpiContext, params) params.subscrNamespace = C.DPI_SUBSCR_NAMESPACE_DBCHANGE params.protocol = C.DPI_SUBSCR_PROTO_CALLBACK params.qos = C.DPI_SUBSCR_QOS_BEST_EFFORT | C.DPI_SUBSCR_QOS_QUERY | C.DPI_SUBSCR_QOS_ROWIDS @@ -151,7 +162,15 @@ func (c *conn) NewSubscription(name string, cb func(Event)) (*Subscription, erro } // typedef void (*dpiSubscrCallback)(void* context, dpiSubscrMessage *message); params.callback = C.dpiSubscrCallback(C.CallbackSubscrDebug) - params.callbackContext = unsafe.Pointer(&subscr) + // cannot pass &subscr to C, so pass indirectly + subscriptionsMu.Lock() + subscriptionsID++ + subscr.ID = subscriptionsID + subscriptions[subscr.ID] = &subscr + subscriptionsMu.Unlock() + subscrID := (*C.uint64_t)(C.malloc(8)) + *subscrID = C.uint64_t(subscriptionsID) + params.callbackContext = unsafe.Pointer(subscrID) dpiSubscr := (*C.dpiSubscr)(C.malloc(C.sizeof_void)) @@ -204,6 +223,9 @@ func (s *Subscription) Register(qry string, params ...interface{}) error { // // This code is EXPERIMENTAL yet! func (s *Subscription) Close() error { + subscriptionsMu.Lock() + delete(subscriptions, s.ID) + subscriptionsMu.Unlock() dpiSubscr := s.dpiSubscr conn := s.conn s.conn = nil diff --git a/vendor/github.com/godror/godror/version.go b/vendor/github.com/godror/godror/version.go index 5dace8e1b09..66059da1141 100644 --- a/vendor/github.com/godror/godror/version.go +++ b/vendor/github.com/godror/godror/version.go @@ -1,4 +1,4 @@ -// Copyright 2019 Tamás Gulácsi. +// Copyright 2020 Tamás Gulácsi. // // SPDX-License-Identifier: UPL-1.0 OR Apache-2.0 @@ -7,4 +7,4 @@ package godror //go:generate bash -c "echo 3.3.0>odpi-version; set -x; curl -L https://github.com/oracle/odpi/archive/v$(cat odpi-version).tar.gz | tar xzvf - odpi-$(cat odpi-version)/{embed,include,src,CONTRIBUTING.md,LICENSE.md,README.md} && rm -rf odpi && mv odpi-$(cat odpi-version) odpi; rm -f odpi-version" // Version of this driver -const Version = "v0.0.1" +const Version = "v0.10.4" diff --git a/vendor/vendor.json b/vendor/vendor.json index 3ee78bf7257..4dca573df82 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -3449,13 +3449,13 @@ "revisionTime": "2019-09-30T11:59:46Z" }, { - "checksumSHA1": "fW8mQy9XfBOcO8dwudie7NHXC5o=", + "checksumSHA1": "xJkfP+WyfKJSBcEa+8T15QjNIr4=", "path": "github.com/godror/godror", - "revision": "446a29355d2967f04604e8e015a3343917860adf", - "revisionTime": "2019-12-12T16:49:28Z", + "revision": "0123d49bd73e1bed106ac8b6af67f943fbbf06e2", + "revisionTime": "2020-01-12T11:05:39Z", "tree": true, - "version": "v0.9.0", - "versionExact": "v0.9.0" + "version": "v0.10.4", + "versionExact": "v0.10.4" }, { "checksumSHA1": "MlaWEe1K+Kpb9wDF88qPoqO1uro=",