|
- /*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
- // Copyright 2011 The Go Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
-
- // Package sql provides a generic interface around SQL (or SQL-like)
- // databases.
- //
- // The sql package must be used in conjunction with a database driver.
- // See https://golang.org/s/sqldrivers for a list of drivers.
- //
- // Drivers that do not support context cancellation will not return until
- // after the query is completed.
- //
- // For usage examples, see the wiki page at
- // https://golang.org/s/sqlwiki.
- package util
-
- import (
- "context"
- "database/sql/driver"
- "errors"
- "fmt"
- "io"
- "sync"
- )
-
- // ScanRows is the result of a query. Its cursor starts before the first row
- // of the result set. Use Next to advance from row to row.
- type ScanRows struct {
- //dc *driverConn // owned; must call releaseConn when closed to release
- dc driver.Conn
- releaseConn func(error)
- rowsi driver.Rows
- cancel func() // called when ScanRows is closed, may be nil.
- //closeStmt *driverStmt // if non-nil, statement to Close on close
-
- // closemu prevents ScanRows from closing while there
- // is an active streaming result. It is held for read during non-close operations
- // and exclusively during close.
- //
- // closemu guards lasterr and closed.
- closemu sync.RWMutex
- closed bool
- lasterr error // non-nil only if closed is true
-
- // lastcols is only used in Scan, Next, and NextResultSet which are expected
- // not to be called concurrently.
- lastcols []driver.Value
- }
-
- func NewScanRows(rowsi driver.Rows) *ScanRows {
- return &ScanRows{rowsi: rowsi}
- }
-
- // lasterrOrErrLocked returns either lasterr or the provided err.
- // rs.closemu must be read-locked.
- func (rs *ScanRows) lasterrOrErrLocked(err error) error {
- if rs.lasterr != nil && rs.lasterr != io.EOF {
- return rs.lasterr
- }
- return err
- }
-
- // bypassRowsAwaitDone is only used for testing.
- // If true, it will not close the ScanRows automatically from the context.
- var bypassRowsAwaitDone = false
-
- func (rs *ScanRows) initContextClose(ctx, txctx context.Context) {
- if ctx.Done() == nil && (txctx == nil || txctx.Done() == nil) {
- return
- }
- if bypassRowsAwaitDone {
- return
- }
- ctx, rs.cancel = context.WithCancel(ctx)
- go rs.awaitDone(ctx, txctx)
- }
-
- // awaitDone blocks until either ctx or txctx is canceled. The ctx is provided
- // from the query context and is canceled when the query ScanRows is closed.
- // If the query was issued in a transaction, the transaction's context
- // is also provided in txctx to ensure ScanRows is closed if the Tx is closed.
- func (rs *ScanRows) awaitDone(ctx, txctx context.Context) {
- var txctxDone <-chan struct{}
- if txctx != nil {
- txctxDone = txctx.Done()
- }
- select {
- case <-ctx.Done():
- case <-txctxDone:
- }
- rs.close(ctx.Err())
- }
-
- // Next prepares the next result row for reading with the Scan method. It
- // returns true on success, or false if there is no next result row or an error
- // happened while preparing it. Err should be consulted to distinguish between
- // the two cases.
- //
- // Every call to Scan, even the first one, must be preceded by a call to Next.
- func (rs *ScanRows) Next() bool {
- var doClose, ok bool
- withLock(rs.closemu.RLocker(), func() {
- doClose, ok = rs.nextLocked()
- })
- if doClose {
- rs.Close()
- }
- return ok
- }
-
- func (rs *ScanRows) nextLocked() (doClose, ok bool) {
- if rs.closed {
- return false, false
- }
-
- // Lock the driver connection before calling the driver interface
- // rowsi to prevent a Tx from rolling back the connection at the same time.
- //rs.dc.Lock()
- //defer rs.dc.Unlock()
-
- if rs.lastcols == nil {
- rs.lastcols = make([]driver.Value, len(rs.rowsi.Columns()))
- }
-
- rs.lasterr = rs.rowsi.Next(rs.lastcols)
- if rs.lasterr != nil {
- // Close the connection if there is a driver error.
- if rs.lasterr != io.EOF {
- return true, false
- }
- nextResultSet, ok := rs.rowsi.(driver.RowsNextResultSet)
- if !ok {
- return true, false
- }
- // The driver is at the end of the current result set.
- // Test to see if there is another result set after the current one.
- // Only close ScanRows if there is no further result sets to read.
- if !nextResultSet.HasNextResultSet() {
- doClose = true
- }
- return doClose, false
- }
- return false, true
- }
-
- // NextResultSet prepares the next result set for reading. It reports whether
- // there is further result sets, or false if there is no further result set
- // or if there is an error advancing to it. The Err method should be consulted
- // to distinguish between the two cases.
- //
- // After calling NextResultSet, the Next method should always be called before
- // scanning. If there are further result sets they may not have rows in the result
- // set.
- func (rs *ScanRows) NextResultSet() bool {
- var doClose bool
- defer func() {
- if doClose {
- rs.Close()
- }
- }()
- rs.closemu.RLock()
- defer rs.closemu.RUnlock()
-
- if rs.closed {
- return false
- }
-
- rs.lastcols = nil
- nextResultSet, ok := rs.rowsi.(driver.RowsNextResultSet)
- if !ok {
- doClose = true
- return false
- }
-
- // Lock the driver connection before calling the driver interface
- // rowsi to prevent a Tx from rolling back the connection at the same time.
- //rs.dc.Lock()
- //defer rs.dc.Unlock()
-
- rs.lasterr = nextResultSet.NextResultSet()
- if rs.lasterr != nil {
- doClose = true
- return false
- }
- return true
- }
-
- // Err returns the error, if any, that was encountered during iteration.
- // Err may be called after an explicit or implicit Close.
- func (rs *ScanRows) Err() error {
- rs.closemu.RLock()
- defer rs.closemu.RUnlock()
- return rs.lasterrOrErrLocked(nil)
- }
-
- //
- var errRowsClosed = errors.New("sql: ScanRows are closed")
-
- // Scan copies the columns in the current row into the values pointed
- // at by dest. The number of values in dest must be the same as the
- // number of columns in ScanRows.
- //
- // Scan converts columns read from the database into the following
- // common Go types and special types provided by the sql package:
- //
- // *string
- // *[]byte
- // *int, *int8, *int16, *int32, *int64
- // *uint, *uint8, *uint16, *uint32, *uint64
- // *bool
- // *float32, *float64
- // *interface{}
- // *RawBytes
- // *ScanRows (cursor value)
- // any type implementing Scanner (see Scanner docs)
- //
- // In the most simple case, if the type of the value from the source
- // column is an integer, bool or string type T and dest is of type *T,
- // Scan simply assigns the value through the pointer.
- //
- // Scan also converts between string and numeric types, as long as no
- // information would be lost. While Scan stringifies all numbers
- // scanned from numeric database columns into *string, scans into
- // numeric types are checked for overflow. For example, a float64 with
- // value 300 or a string with value "300" can scan into a uint16, but
- // not into a uint8, though float64(255) or "255" can scan into a
- // uint8. One exception is that scans of some float64 numbers to
- // strings may lose information when stringifying. In general, scan
- // floating point columns into *float64.
- //
- // If a dest argument has type *[]byte, Scan saves in that argument a
- // copy of the corresponding data. The copy is owned by the caller and
- // can be modified and held indefinitely. The copy can be avoided by
- // using an argument of type *RawBytes instead; see the documentation
- // for RawBytes for restrictions on its use.
- //
- // If an argument has type *interface{}, Scan copies the value
- // provided by the underlying driver without conversion. When scanning
- // from a source value of type []byte to *interface{}, a copy of the
- // slice is made and the caller owns the result.
- //
- // Source values of type time.Time may be scanned into values of type
- // *time.Time, *interface{}, *string, or *[]byte. When converting to
- // the latter two, time.RFC3339Nano is used.
- //
- // Source values of type bool may be scanned into types *bool,
- // *interface{}, *string, *[]byte, or *RawBytes.
- //
- // For scanning into *bool, the source may be true, false, 1, 0, or
- // string inputs parseable by strconv.ParseBool.
- //
- // Scan can also convert a cursor returned from a query, such as
- // "select cursor(select * from my_table) from dual", into a
- // *ScanRows value that can itself be scanned from. The parent
- // select query will close any cursor *ScanRows if the parent *ScanRows is closed.
- //
- // If any of the first arguments implementing Scanner returns an error,
- // that error will be wrapped in the returned error
- func (rs *ScanRows) Scan(dest ...interface{}) error {
- rs.closemu.RLock()
-
- if rs.lasterr != nil && rs.lasterr != io.EOF {
- rs.closemu.RUnlock()
- return rs.lasterr
- }
- if rs.closed {
- err := rs.lasterrOrErrLocked(errRowsClosed)
- rs.closemu.RUnlock()
- return err
- }
- rs.closemu.RUnlock()
-
- if rs.lastcols == nil {
- return errors.New("sql: Scan called without calling Next")
- }
- if len(dest) != len(rs.lastcols) {
- return fmt.Errorf("sql: expected %d destination arguments in Scan, not %d", len(rs.lastcols), len(dest))
- }
- for i, sv := range rs.lastcols {
- err := convertAssignRows(dest[i], sv, rs)
- if err != nil {
- return fmt.Errorf(`sql: Scan error on column index %d, name %q: %w`, i, rs.rowsi.Columns()[i], err)
- }
- }
- return nil
- }
-
- // rowsCloseHook returns a function so tests may install the
- // hook through a test only mutex.
- var rowsCloseHook = func() func(*ScanRows, *error) { return nil }
-
- // Close closes the ScanRows, preventing further enumeration. If Next is called
- // and returns false and there are no further result sets,
- // the ScanRows are closed automatically and it will suffice to check the
- // result of Err. Close is idempotent and does not affect the result of Err.
- func (rs *ScanRows) Close() error {
- //return rs.close(nil)
- return nil
- }
-
- func (rs *ScanRows) close(err error) error {
- rs.closemu.Lock()
- defer rs.closemu.Unlock()
-
- if rs.closed {
- return nil
- }
- rs.closed = true
-
- if rs.lasterr == nil {
- rs.lasterr = err
- }
-
- err = rs.rowsi.Close()
- //withLock(rs.dc, func() {
- // err = rs.rowsi.Close()
- //})
- if fn := rowsCloseHook(); fn != nil {
- fn(rs, &err)
- }
- if rs.cancel != nil {
- rs.cancel()
- }
-
- //if rs.closeStmt != nil {
- // rs.closeStmt.Close()
- //}
- rs.releaseConn(err)
- return err
- }
-
- // withLock runs while holding lk.
- func withLock(lk sync.Locker, fn func()) {
- lk.Lock()
- defer lk.Unlock() // in case fn panics
- fn()
- }
|