Source File
sql.go
Belonging Package
database/sql
type NamedArg struct {
_Named_Fields_Required struct{}
Value interface{}
}
type IsolationLevel int
func ( IsolationLevel) () string {
switch {
case LevelDefault:
return "Default"
case LevelReadUncommitted:
return "Read Uncommitted"
case LevelReadCommitted:
return "Read Committed"
case LevelWriteCommitted:
return "Write Committed"
case LevelRepeatableRead:
return "Repeatable Read"
case LevelSnapshot:
return "Snapshot"
case LevelSerializable:
return "Serializable"
case LevelLinearizable:
return "Linearizable"
default:
return "IsolationLevel(" + strconv.Itoa(int()) + ")"
}
}
var _ fmt.Stringer = LevelDefault
type NullString struct {
String string
Valid bool // Valid is true if String is not NULL
}
type NullFloat64 struct {
Float64 float64
Valid bool // Valid is true if Float64 is not NULL
}
Scan(src interface{}) error
}
type Out struct {
_Named_Fields_Required struct{}
Dest interface{}
waitDuration int64 // Total time waited for new connections.
numClosed uint64
mu sync.Mutex // protects following fields
freeConn []*driverConn
connRequests map[uint64]chan connRequest
nextRequest uint64 // Next key to use in connRequests.
openerCh chan struct{}
closed bool
dep map[finalCloser]depSet
lastPut map[*driverConn]string // stacktrace of last conn's put; debug only
maxIdleCount int // zero means defaultMaxIdleConns; negative means 0
maxOpen int // <= 0 means unlimited
maxLifetime time.Duration // maximum amount of time a connection may be reused
maxIdleTime time.Duration // maximum amount of time a connection may be idle before being closed
cleanerCh chan struct{}
waitCount int64 // Total number of connections waited for.
maxIdleClosed int64 // Total number of connections closed due to idle count.
maxIdleTimeClosed int64 // Total number of connections closed due to idle time.
maxLifetimeClosed int64 // Total number of connections closed due to max connection lifetime limit.
stop func() // stop cancels the connection opener.
}
type connReuseStrategy uint8
inUse bool
returnedAt time.Time // Time the connection was created or returned.
onPut []func() // code (with db.mu held) run when conn is next returned
dbmuClosed bool // same as closed, but guarded by db.mu, for removeClosedStmtLocked
}
func ( *driverConn) ( error) {
.db.putConn(, , true)
}
func ( *driverConn) ( *driverStmt) {
.Lock()
defer .Unlock()
delete(.openStmt, )
}
func ( *driverConn) ( time.Duration) bool {
if <= 0 {
return false
}
return .createdAt.Add().Before(nowFunc())
}
func ( *driverConn) ( context.Context) error {
.Lock()
defer .Unlock()
if !.needReset {
return nil
}
if , := .ci.(driver.SessionResetter); {
return .ResetSession()
}
return nil
}
func ( *driverConn) ( context.Context, stmtConnGrabber, string) (*driverStmt, error) {
, := ctxDriverPrepare(, .ci, )
if != nil {
return nil,
}
:= &driverStmt{Locker: , si: }
func ( *driverConn) () func() error {
.Lock()
defer .Unlock()
if .closed {
return func() error { return errors.New("sql: duplicate driverConn close") }
}
.closed = true
return .db.removeDepLocked(, )
}
func ( *driverConn) () error {
.Lock()
if .closed {
.Unlock()
return errors.New("sql: duplicate driverConn close")
}
.closed = true
.Unlock() // not defer; removeDep finalClose calls may need to lock
.db.mu.Lock()
.dbmuClosed = true
:= .db.removeDepLocked(, )
.db.mu.Unlock()
return ()
}
func ( *driverConn) () error {
var error
var []*driverStmt
withLock(, func() {
= make([]*driverStmt, 0, len(.openStmt))
for := range .openStmt {
= append(, )
}
.openStmt = nil
})
for , := range {
.Close()
}
withLock(, func() {
.finalClosed = true
= .ci.Close()
.ci = nil
})
.db.mu.Lock()
.db.numOpen--
.db.maybeOpenNewConnections()
.db.mu.Unlock()
atomic.AddUint64(&.db.numClosed, 1)
return
}
finalClose() error
}
delete(.dep, )
return .finalClose
var connectionRequestQueueSize = 1000000
type dsnConnector struct {
dsn string
driver driver.Driver
}
func ( dsnConnector) ( context.Context) (driver.Conn, error) {
return .driver.Open(.dsn)
}
func ( dsnConnector) () driver.Driver {
return .driver
}
func ( driver.Connector) *DB {
, := context.WithCancel(context.Background())
:= &DB{
connector: ,
openerCh: make(chan struct{}, connectionRequestQueueSize),
lastPut: make(map[*driverConn]string),
connRequests: make(map[uint64]chan connRequest),
stop: ,
}
go .connectionOpener()
return
}
func (, string) (*DB, error) {
driversMu.RLock()
, := drivers[]
driversMu.RUnlock()
if ! {
return nil, fmt.Errorf("sql: unknown driver %q (forgotten import?)", )
}
if , := .(driver.DriverContext); {
, := .OpenConnector()
if != nil {
return nil,
}
return OpenDB(), nil
}
return OpenDB(dsnConnector{dsn: , driver: }), nil
}
func ( *DB) ( context.Context, *driverConn, func(error)) error {
var error
if , := .ci.(driver.Pinger); {
withLock(, func() {
= .Ping()
})
}
()
return
}
func ( *DB) ( context.Context) error {
var *driverConn
var error
for := 0; < maxBadConnRetries; ++ {
, = .conn(, cachedOrNewConn)
if != driver.ErrBadConn {
break
}
}
if == driver.ErrBadConn {
, = .conn(, alwaysNewConn)
}
if != nil {
return
}
return .pingDC(, , .releaseConn)
}
func ( *DB) () error {
return .PingContext(context.Background())
}
func ( *DB) () error {
.mu.Lock()
if .closed { // Make DB.Close idempotent
.mu.Unlock()
return nil
}
if .cleanerCh != nil {
close(.cleanerCh)
}
var error
:= make([]func() error, 0, len(.freeConn))
for , := range .freeConn {
= append(, .closeDBLocked())
}
.freeConn = nil
.closed = true
for , := range .connRequests {
close()
}
.mu.Unlock()
for , := range {
:= ()
if != nil {
=
}
}
.stop()
return
}
const defaultMaxIdleConns = 2
func ( *DB) () int {
:= .maxIdleCount
switch {
return defaultMaxIdleConns
case < 0:
return 0
default:
return
}
}
func ( *DB) () time.Duration {
if .maxIdleTime <= 0 {
return .maxLifetime
}
if .maxLifetime <= 0 {
return .maxIdleTime
}
:= .maxIdleTime
if > .maxLifetime {
= .maxLifetime
}
return
}
func ( *DB) ( int) {
.mu.Lock()
if > 0 {
.maxIdleCount =
.maxIdleCount = -1
if .maxOpen > 0 && .maxIdleConnsLocked() > .maxOpen {
.maxIdleCount = .maxOpen
}
var []*driverConn
:= len(.freeConn)
:= .maxIdleConnsLocked()
if > {
= .freeConn[:]
.freeConn = .freeConn[:]
}
.maxIdleClosed += int64(len())
.mu.Unlock()
for , := range {
.Close()
}
}
if > 0 && < .maxLifetime && .cleanerCh != nil {
select {
case .cleanerCh <- struct{}{}:
default:
}
}
.maxLifetime =
.startCleanerLocked()
.mu.Unlock()
}
if > 0 && < .maxIdleTime && .cleanerCh != nil {
select {
case .cleanerCh <- struct{}{}:
default:
}
}
.maxIdleTime =
.startCleanerLocked()
}
func ( *DB) () {
if (.maxLifetime > 0 || .maxIdleTime > 0) && .numOpen > 0 && .cleanerCh == nil {
.cleanerCh = make(chan struct{}, 1)
go .connectionCleaner(.shortestIdleTimeLocked())
}
}
func ( *DB) ( time.Duration) {
const = time.Second
if < {
=
}
:= time.NewTimer()
for {
select {
case <-.C:
case <-.cleanerCh: // maxLifetime was changed or db was closed.
}
.mu.Lock()
= .shortestIdleTimeLocked()
if .closed || .numOpen == 0 || <= 0 {
.cleanerCh = nil
.mu.Unlock()
return
}
:= .connectionCleanerRunLocked()
.mu.Unlock()
for , := range {
.Close()
}
if < {
=
}
.Reset()
}
}
func ( *DB) () ( []*driverConn) {
if .maxLifetime > 0 {
:= nowFunc().Add(-.maxLifetime)
for := 0; < len(.freeConn); ++ {
:= .freeConn[]
if .createdAt.Before() {
= append(, )
:= len(.freeConn) - 1
.freeConn[] = .freeConn[]
.freeConn[] = nil
.freeConn = .freeConn[:]
--
}
}
.maxLifetimeClosed += int64(len())
}
if .maxIdleTime > 0 {
:= nowFunc().Add(-.maxIdleTime)
var int64
for := 0; < len(.freeConn); ++ {
:= .freeConn[]
if .maxIdleTime > 0 && .returnedAt.Before() {
= append(, )
++
:= len(.freeConn) - 1
.freeConn[] = .freeConn[]
.freeConn[] = nil
.freeConn = .freeConn[:]
--
}
}
.maxIdleTimeClosed +=
}
return
}
type DBStats struct {
MaxOpenConnections int // Maximum number of open connections to the database.
WaitCount int64 // The total number of connections waited for.
WaitDuration time.Duration // The total time blocked waiting for a new connection.
MaxIdleClosed int64 // The total number of connections closed due to SetMaxIdleConns.
MaxIdleTimeClosed int64 // The total number of connections closed due to SetConnMaxIdleTime.
MaxLifetimeClosed int64 // The total number of connections closed due to SetConnMaxLifetime.
}
func ( *DB) () DBStats {
:= atomic.LoadInt64(&.waitDuration)
.mu.Lock()
defer .mu.Unlock()
:= DBStats{
MaxOpenConnections: .maxOpen,
Idle: len(.freeConn),
OpenConnections: .numOpen,
InUse: .numOpen - len(.freeConn),
WaitCount: .waitCount,
WaitDuration: time.Duration(),
MaxIdleClosed: .maxIdleClosed,
MaxIdleTimeClosed: .maxIdleTimeClosed,
MaxLifetimeClosed: .maxLifetimeClosed,
}
return
}
, := .connector.Connect()
.mu.Lock()
defer .mu.Unlock()
if .closed {
if == nil {
.Close()
}
.numOpen--
return
}
if != nil {
.numOpen--
.putConnDBLocked(nil, )
.maybeOpenNewConnections()
return
}
:= &driverConn{
db: ,
createdAt: nowFunc(),
returnedAt: nowFunc(),
ci: ,
}
if .putConnDBLocked(, ) {
.addDepLocked(, )
} else {
.numOpen--
.Close()
}
}
type connRequest struct {
conn *driverConn
err error
}
var errDBClosed = errors.New("sql: database is closed")
func ( *DB) () uint64 {
:= .nextRequest
.nextRequest++
return
}
func ( *DB) ( context.Context, connReuseStrategy) (*driverConn, error) {
.mu.Lock()
if .closed {
.mu.Unlock()
return nil, errDBClosed
if := .resetSession(); == driver.ErrBadConn {
.Close()
return nil, driver.ErrBadConn
}
return , nil
}
:= make(chan connRequest, 1)
:= .nextRequestKeyLocked()
.connRequests[] =
.waitCount++
.mu.Unlock()
:= nowFunc()
select {
.mu.Lock()
delete(.connRequests, )
.mu.Unlock()
atomic.AddInt64(&.waitDuration, int64(time.Since()))
select {
default:
case , := <-:
if && .conn != nil {
.putConn(.conn, .err, false)
}
}
return nil, .Err()
case , := <-:
atomic.AddInt64(&.waitDuration, int64(time.Since()))
if ! {
return nil, errDBClosed
if := .conn.resetSession(); == driver.ErrBadConn {
.conn.Close()
return nil, driver.ErrBadConn
}
return .conn, .err
}
}
.numOpen++ // optimistically
.mu.Unlock()
, := .connector.Connect()
if != nil {
.mu.Lock()
.numOpen-- // correct for earlier optimism
.maybeOpenNewConnections()
.mu.Unlock()
return nil,
}
.mu.Lock()
:= &driverConn{
db: ,
createdAt: nowFunc(),
returnedAt: nowFunc(),
ci: ,
inUse: true,
}
.addDepLocked(, )
.mu.Unlock()
return , nil
}
var putConnHook func(*DB, *driverConn)
const debugGetPut = false
func ( *DB) ( *driverConn, error, bool) {
if != driver.ErrBadConn {
if !.validateConnection() {
= driver.ErrBadConn
}
}
.mu.Lock()
if !.inUse {
.mu.Unlock()
if debugGetPut {
fmt.Printf("putConn(%v) DUPLICATE was: %s\n\nPREVIOUS was: %s", , stack(), .lastPut[])
}
panic("sql: connection returned that was never out")
}
if != driver.ErrBadConn && .expired(.maxLifetime) {
.maxLifetimeClosed++
= driver.ErrBadConn
}
if debugGetPut {
.lastPut[] = stack()
}
.inUse = false
.returnedAt = nowFunc()
for , := range .onPut {
()
}
.onPut = nil
.maybeOpenNewConnections()
.mu.Unlock()
.Close()
return
}
if putConnHook != nil {
putConnHook(, )
}
:= .putConnDBLocked(, nil)
.mu.Unlock()
if ! {
.Close()
return
}
}
func ( *DB) ( *driverConn, error) bool {
if .closed {
return false
}
if .maxOpen > 0 && .numOpen > .maxOpen {
return false
}
if := len(.connRequests); > 0 {
var chan connRequest
var uint64
for , = range .connRequests {
break
}
delete(.connRequests, ) // Remove from pending requests.
if == nil {
.inUse = true
}
<- connRequest{
conn: ,
err: ,
}
return true
} else if == nil && !.closed {
if .maxIdleConnsLocked() > len(.freeConn) {
.freeConn = append(.freeConn, )
.startCleanerLocked()
return true
}
.maxIdleClosed++
}
return false
}
const maxBadConnRetries = 2
func ( *DB) ( context.Context, string) (*Stmt, error) {
var *Stmt
var error
for := 0; < maxBadConnRetries; ++ {
, = .prepare(, , cachedOrNewConn)
if != driver.ErrBadConn {
break
}
}
if == driver.ErrBadConn {
return .prepare(, , alwaysNewConn)
}
return ,
}
func ( *DB) ( string) (*Stmt, error) {
return .PrepareContext(context.Background(), )
}
func ( *DB) ( context.Context, *driverConn, func(error), stmtConnGrabber, string) (*Stmt, error) {
var *driverStmt
var error
defer func() {
()
}()
withLock(, func() {
, = .prepareLocked(, , )
})
if != nil {
return nil,
}
:= &Stmt{
db: ,
query: ,
cg: ,
cgds: ,
}
if == nil {
.css = []connStmt{{, }}
.lastNumClosed = atomic.LoadUint64(&.numClosed)
.addDep(, )
}
return , nil
}
func ( *DB) ( context.Context, string, ...interface{}) (Result, error) {
var Result
var error
for := 0; < maxBadConnRetries; ++ {
, = .exec(, , , cachedOrNewConn)
if != driver.ErrBadConn {
break
}
}
if == driver.ErrBadConn {
return .exec(, , , alwaysNewConn)
}
return ,
}
func ( *DB) ( string, ...interface{}) (Result, error) {
return .ExecContext(context.Background(), , ...)
}
func ( *DB) ( context.Context, string, []interface{}, connReuseStrategy) (Result, error) {
, := .conn(, )
if != nil {
return nil,
}
return .execDC(, , .releaseConn, , )
}
func ( *DB) ( context.Context, *driverConn, func(error), string, []interface{}) ( Result, error) {
defer func() {
()
}()
, := .ci.(driver.ExecerContext)
var driver.Execer
if ! {
, = .ci.(driver.Execer)
}
if {
var []driver.NamedValue
var driver.Result
withLock(, func() {
, = driverArgsConnLocked(.ci, nil, )
if != nil {
return
}
, = ctxDriverExec(, , , , )
})
if != driver.ErrSkip {
if != nil {
return nil,
}
return driverResult{, }, nil
}
}
var driver.Stmt
withLock(, func() {
, = ctxDriverPrepare(, .ci, )
})
if != nil {
return nil,
}
:= &driverStmt{Locker: , si: }
defer .Close()
return resultFromStatement(, .ci, , ...)
}
func ( *DB) ( context.Context, string, ...interface{}) (*Rows, error) {
var *Rows
var error
for := 0; < maxBadConnRetries; ++ {
, = .query(, , , cachedOrNewConn)
if != driver.ErrBadConn {
break
}
}
if == driver.ErrBadConn {
return .query(, , , alwaysNewConn)
}
return ,
}
func ( *DB) ( string, ...interface{}) (*Rows, error) {
return .QueryContext(context.Background(), , ...)
}
func ( *DB) ( context.Context, string, []interface{}, connReuseStrategy) (*Rows, error) {
, := .conn(, )
if != nil {
return nil,
}
return .queryDC(, nil, , .releaseConn, , )
}
func ( *DB) (, context.Context, *driverConn, func(error), string, []interface{}) (*Rows, error) {
, := .ci.(driver.QueryerContext)
var driver.Queryer
if ! {
, = .ci.(driver.Queryer)
}
if {
var []driver.NamedValue
var driver.Rows
var error
withLock(, func() {
, = driverArgsConnLocked(.ci, nil, )
if != nil {
return
}
, = ctxDriverQuery(, , , , )
})
if != driver.ErrSkip {
if != nil {
()
return nil,
:= &Rows{
dc: ,
releaseConn: ,
rowsi: ,
}
.initContextClose(, )
return , nil
}
}
var driver.Stmt
var error
withLock(, func() {
, = ctxDriverPrepare(, .ci, )
})
if != nil {
()
return nil,
}
:= &driverStmt{Locker: , si: }
, := rowsiFromStatement(, .ci, , ...)
if != nil {
.Close()
()
return nil,
}
:= &Rows{
dc: ,
releaseConn: ,
rowsi: ,
closeStmt: ,
}
.initContextClose(, )
return , nil
}
func ( *DB) ( string, ...interface{}) *Row {
return .QueryRowContext(context.Background(), , ...)
}
func ( *DB) ( context.Context, *TxOptions) (*Tx, error) {
var *Tx
var error
for := 0; < maxBadConnRetries; ++ {
, = .begin(, , cachedOrNewConn)
if != driver.ErrBadConn {
break
}
}
if == driver.ErrBadConn {
return .begin(, , alwaysNewConn)
}
return ,
}
, := context.WithCancel()
= &Tx{
db: ,
dc: ,
releaseConn: ,
txi: ,
cancel: ,
keepConnOnRollback: ,
ctx: ,
}
go .awaitDone()
return , nil
}
var ErrConnDone = errors.New("sql: connection is already closed")
func ( *DB) ( context.Context) (*Conn, error) {
var *driverConn
var error
for := 0; < maxBadConnRetries; ++ {
, = .conn(, cachedOrNewConn)
if != driver.ErrBadConn {
break
}
}
if == driver.ErrBadConn {
, = .conn(, alwaysNewConn)
}
if != nil {
return nil,
}
:= &Conn{
db: ,
dc: ,
}
return , nil
}
type releaseConn func(error)
func ( *Conn) (context.Context) (*driverConn, releaseConn, error) {
if atomic.LoadInt32(&.done) != 0 {
return nil, nil, ErrConnDone
}
.closemu.RLock()
return .dc, .closemuRUnlockCondReleaseConn, nil
}
func ( *Conn) ( func( interface{}) error) ( error) {
var *driverConn
var releaseConn
if {
= driver.ErrBadConn
}
()
}()
= (.ci)
= false
return
}
releaseConn func(error)
cancel func()
var hookTxGrabConn func()
func ( *Tx) ( context.Context) (*driverConn, releaseConn, error) {
select {
default:
case <-.Done():
return nil, nil, .Err()
}
.cancel()
.closemu.Lock()
.closemu.Unlock()
var error
withLock(.dc, func() {
= .txi.Commit()
})
if != driver.ErrBadConn {
.closePrepared()
}
.close()
return
}
var rollbackHook func()
func ( *Tx) ( bool) error {
if !atomic.CompareAndSwapInt32(&.done, 0, 1) {
return ErrTxDone
}
if rollbackHook != nil {
rollbackHook()
}
func ( *Tx) ( string) (*Stmt, error) {
return .PrepareContext(context.Background(), )
}
for , := range .css {
if .dc == {
= .ds.si
break
}
}
.mu.Unlock()
if == nil {
var *driverStmt
withLock(, func() {
, = .prepareOnConnLocked(, )
})
if != nil {
return &Stmt{stickyErr: }
}
= .si
}
=
}
:= &Stmt{
db: .db,
cg: ,
cgds: &driverStmt{
Locker: ,
si: ,
},
parentStmt: ,
query: .query,
}
if != nil {
.db.addDep(, )
}
.stmts.Lock()
.stmts.v = append(.stmts.v, )
.stmts.Unlock()
return
}
func ( *Tx) ( *Stmt) *Stmt {
return .StmtContext(context.Background(), )
}
func ( *Tx) ( string, ...interface{}) (Result, error) {
return .ExecContext(context.Background(), , ...)
}
func ( *Tx) ( string, ...interface{}) (*Rows, error) {
return .QueryContext(context.Background(), , ...)
}
func ( *Tx) ( string, ...interface{}) *Row {
return .QueryRowContext(context.Background(), , ...)
}
type connStmt struct {
dc *driverConn
ds *driverStmt
}
grabConn(context.Context) (*driverConn, releaseConn, error)
txCtx() context.Context
}
var (
_ stmtConnGrabber = &Tx{}
_ stmtConnGrabber = &Conn{}
)
func ( *Stmt) ( context.Context, ...interface{}) (Result, error) {
.closemu.RLock()
defer .closemu.RUnlock()
var Result
:= cachedOrNewConn
for := 0; < maxBadConnRetries+1; ++ {
if == maxBadConnRetries {
= alwaysNewConn
}
, , , := .connStmt(, )
if != nil {
if == driver.ErrBadConn {
continue
}
return nil,
}
, = resultFromStatement(, .ci, , ...)
()
if != driver.ErrBadConn {
return ,
}
}
return nil, driver.ErrBadConn
}
func ( *Stmt) ( ...interface{}) (Result, error) {
return .ExecContext(context.Background(), ...)
}
func ( context.Context, driver.Conn, *driverStmt, ...interface{}) (Result, error) {
.Lock()
defer .Unlock()
, := driverArgsConnLocked(, , )
if != nil {
return nil,
}
, := ctxDriverStmtExec(, .si, )
if != nil {
return nil,
}
return driverResult{.Locker, }, nil
}
if .cg != nil {
.mu.Unlock()
, , = .cg.grabConn() // blocks, waiting for the connection.
if != nil {
return
}
return , , .cgds, nil
}
.removeClosedStmtLocked()
.mu.Unlock()
, = .db.conn(, )
if != nil {
return nil, nil, nil,
}
.mu.Lock()
for , := range .css {
if .dc == {
.mu.Unlock()
return , .releaseConn, .ds, nil
}
}
.mu.Unlock()
withLock(, func() {
, = .prepareOnConnLocked(, )
})
if != nil {
.releaseConn()
return nil, nil, nil,
}
return , .releaseConn, , nil
}
func ( *Stmt) ( context.Context, ...interface{}) (*Rows, error) {
.closemu.RLock()
defer .closemu.RUnlock()
var driver.Rows
:= cachedOrNewConn
for := 0; < maxBadConnRetries+1; ++ {
if == maxBadConnRetries {
= alwaysNewConn
}
, , , := .connStmt(, )
if != nil {
if == driver.ErrBadConn {
continue
}
return nil,
}
, = rowsiFromStatement(, .ci, , ...)
.releaseConn = func( error) {
()
.db.removeDep(, )
}
var context.Context
if .cg != nil {
= .cg.txCtx()
}
.initContextClose(, )
return , nil
}
()
if != driver.ErrBadConn {
return nil,
}
}
return nil, driver.ErrBadConn
}
func ( *Stmt) ( ...interface{}) (*Rows, error) {
return .QueryContext(context.Background(), ...)
}
func ( context.Context, driver.Conn, *driverStmt, ...interface{}) (driver.Rows, error) {
.Lock()
defer .Unlock()
, := driverArgsConnLocked(, , )
if != nil {
return nil,
}
return ctxDriverStmtQuery(, .si, )
}
func ( *Stmt) ( ...interface{}) *Row {
return .QueryRowContext(context.Background(), ...)
}
type Rows struct {
dc *driverConn // owned; must call releaseConn when closed to release
releaseConn func(error)
rowsi driver.Rows
cancel func() // called when Rows is closed, may be nil.
closeStmt *driverStmt // if non-nil, statement to Close on close
var bypassRowsAwaitDone = false
func ( *Rows) (, context.Context) {
if .Done() == nil && ( == nil || .Done() == nil) {
return
}
if bypassRowsAwaitDone {
return
}
, .cancel = context.WithCancel()
go .awaitDone(, )
}
if !.HasNextResultSet() {
= true
}
return , false
}
return false, true
}
func ( *Rows) () ([]*ColumnType, error) {
.closemu.RLock()
defer .closemu.RUnlock()
if .closed {
return nil, .lasterrOrErrLocked(errRowsClosed)
}
if .rowsi == nil {
return nil, .lasterrOrErrLocked(errNoRows)
}
.dc.Lock()
defer .dc.Unlock()
return rowsColumnInfoSetupConnLocked(.rowsi), nil
}
func ( *ColumnType) () string {
return .name
}
func ( *ColumnType) () ( int64, bool) {
return .length, .hasLength
}
func ( *ColumnType) () (, int64, bool) {
return .precision, .scale, .hasPrecisionScale
}
func ( *ColumnType) () reflect.Type {
return .scanType
}
func ( *ColumnType) () (, bool) {
return .nullable, .hasNullable
}
func ( *ColumnType) () string {
return .databaseType
}
func ( driver.Rows) []*ColumnType {
:= .Columns()
:= make([]*ColumnType, len())
for := range {
:= &ColumnType{
name: [],
}
[] =
if , := .(driver.RowsColumnTypeScanType); {
.scanType = .ColumnTypeScanType()
} else {
.scanType = reflect.TypeOf(new(interface{})).Elem()
}
if , := .(driver.RowsColumnTypeDatabaseTypeName); {
.databaseType = .ColumnTypeDatabaseTypeName()
}
if , := .(driver.RowsColumnTypeLength); {
.length, .hasLength = .ColumnTypeLength()
}
if , := .(driver.RowsColumnTypeNullable); {
.nullable, .hasNullable = .ColumnTypeNullable()
}
if , := .(driver.RowsColumnTypePrecisionScale); {
.precision, .scale, .hasPrecisionScale = .ColumnTypePrecisionScale()
}
}
return
}
func ( *Rows) ( ...interface{}) error {
.closemu.RLock()
if .lasterr != nil && .lasterr != io.EOF {
.closemu.RUnlock()
return .lasterr
}
if .closed {
:= .lasterrOrErrLocked(errRowsClosed)
.closemu.RUnlock()
return
}
.closemu.RUnlock()
if .lastcols == nil {
return errors.New("sql: Scan called without calling Next")
}
if len() != len(.lastcols) {
return fmt.Errorf("sql: expected %d destination arguments in Scan, not %d", len(.lastcols), len())
}
for , := range .lastcols {
:= convertAssignRows([], , )
if != nil {
return fmt.Errorf(`sql: Scan error on column index %d, name %q: %w`, , .rowsi.Columns()[], )
}
}
return nil
}
var rowsCloseHook = func() func(*Rows, *error) { return nil }
func ( *Rows) () error {
return .close(nil)
}
func ( *Rows) ( error) error {
.closemu.Lock()
defer .closemu.Unlock()
if .closed {
return nil
}
.closed = true
if .lasterr == nil {
.lasterr =
}
withLock(.dc, func() {
= .rowsi.Close()
})
if := rowsCloseHook(); != nil {
(, &)
}
if .cancel != nil {
.cancel()
}
if .closeStmt != nil {
.closeStmt.Close()
}
.releaseConn()
return
}
RowsAffected() (int64, error)
}
type driverResult struct {
sync.Locker // the *driverConn
resi driver.Result
}
func ( driverResult) () (int64, error) {
.Lock()
defer .Unlock()
return .resi.LastInsertId()
}
func ( driverResult) () (int64, error) {
.Lock()
defer .Unlock()
return .resi.RowsAffected()
}
func () string {
var [2 << 10]byte
return string([:runtime.Stack([:], false)])
}
![]() |
The pages are generated with Golds v0.3.2-preview. (GOOS=darwin GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds. |