diff options
author | Ian Lance Taylor <iant@golang.org> | 2018-09-24 21:46:21 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2018-09-24 21:46:21 +0000 |
commit | dd931d9b48647e898dc80927c532ae93cc09e192 (patch) | |
tree | 71be2295cd79b8a182f6130611658db8628772d5 /libgo/go/database | |
parent | 779d8a5ad09b01428726ea5a0e6c87bd9ac3c0e4 (diff) | |
download | gcc-dd931d9b48647e898dc80927c532ae93cc09e192.tar.gz |
libgo: update to Go 1.11
Reviewed-on: https://go-review.googlesource.com/136435
gotools/:
* Makefile.am (mostlyclean-local): Run chmod on check-go-dir to
make sure it is writable.
(check-go-tools): Likewise.
(check-vet): Copy internal/objabi to check-vet-dir.
* Makefile.in: Rebuild.
From-SVN: r264546
Diffstat (limited to 'libgo/go/database')
-rw-r--r-- | libgo/go/database/sql/convert.go | 12 | ||||
-rw-r--r-- | libgo/go/database/sql/fakedb_test.go | 25 | ||||
-rw-r--r-- | libgo/go/database/sql/sql.go | 182 | ||||
-rw-r--r-- | libgo/go/database/sql/sql_test.go | 57 |
4 files changed, 183 insertions, 93 deletions
diff --git a/libgo/go/database/sql/convert.go b/libgo/go/database/sql/convert.go index b79ec3f7b27..92a2ebe0e99 100644 --- a/libgo/go/database/sql/convert.go +++ b/libgo/go/database/sql/convert.go @@ -379,10 +379,9 @@ func convertAssign(dest, src interface{}) error { if src == nil { dv.Set(reflect.Zero(dv.Type())) return nil - } else { - dv.Set(reflect.New(dv.Type().Elem())) - return convertAssign(dv.Interface(), src) } + dv.Set(reflect.New(dv.Type().Elem())) + return convertAssign(dv.Interface(), src) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: s := asString(src) i64, err := strconv.ParseInt(s, 10, dv.Type().Bits()) @@ -434,11 +433,10 @@ func strconvErr(err error) error { func cloneBytes(b []byte) []byte { if b == nil { return nil - } else { - c := make([]byte, len(b)) - copy(c, b) - return c } + c := make([]byte, len(b)) + copy(c, b) + return c } func asString(src interface{}) string { diff --git a/libgo/go/database/sql/fakedb_test.go b/libgo/go/database/sql/fakedb_test.go index abb8d40fc02..a21bae61bac 100644 --- a/libgo/go/database/sql/fakedb_test.go +++ b/libgo/go/database/sql/fakedb_test.go @@ -296,10 +296,10 @@ func (db *fakeDB) createTable(name string, columnNames, columnTypes []string) er db.tables = make(map[string]*table) } if _, exist := db.tables[name]; exist { - return fmt.Errorf("table %q already exists", name) + return fmt.Errorf("fakedb: table %q already exists", name) } if len(columnNames) != len(columnTypes) { - return fmt.Errorf("create table of %q len(names) != len(types): %d vs %d", + return fmt.Errorf("fakedb: create table of %q len(names) != len(types): %d vs %d", name, len(columnNames), len(columnTypes)) } db.tables[name] = &table{colname: columnNames, coltype: columnTypes} @@ -365,7 +365,7 @@ func (c *fakeConn) Begin() (driver.Tx, error) { return nil, driver.ErrBadConn } if c.currTx != nil { - return nil, errors.New("already in a transaction") + return nil, errors.New("fakedb: already in a transaction") } c.touchMem() c.currTx = &fakeTx{c: c} @@ -419,13 +419,13 @@ func (c *fakeConn) Close() (err error) { }() c.touchMem() if c.currTx != nil { - return errors.New("can't close fakeConn; in a Transaction") + return errors.New("fakedb: can't close fakeConn; in a Transaction") } if c.db == nil { - return errors.New("can't close fakeConn; already closed") + return errors.New("fakedb: can't close fakeConn; already closed") } if c.stmtsMade > c.stmtsClosed { - return errors.New("can't close; dangling statement(s)") + return errors.New("fakedb: can't close; dangling statement(s)") } c.db = nil return nil @@ -437,7 +437,7 @@ func checkSubsetTypes(allowAny bool, args []driver.NamedValue) error { case int64, float64, bool, nil, []byte, string, time.Time: default: if !allowAny { - return fmt.Errorf("fakedb_test: invalid argument ordinal %[1]d: %[2]v, type %[2]T", arg.Ordinal, arg.Value) + return fmt.Errorf("fakedb: invalid argument ordinal %[1]d: %[2]v, type %[2]T", arg.Ordinal, arg.Value) } } } @@ -729,7 +729,7 @@ func (s *fakeStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (d return nil, driver.ErrBadConn } if s.c.isDirtyAndMark() { - return nil, errors.New("session is dirty") + return nil, errors.New("fakedb: session is dirty") } err := checkSubsetTypes(s.c.db.allowAny, args) @@ -765,8 +765,7 @@ func (s *fakeStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (d // Used for some of the concurrent tests. return s.execInsert(args, false) } - fmt.Printf("EXEC statement, cmd=%q: %#v\n", s.cmd, s) - return nil, fmt.Errorf("unimplemented statement Exec command type of %q", s.cmd) + return nil, fmt.Errorf("fakedb: unimplemented statement Exec command type of %q", s.cmd) } // When doInsert is true, add the row to the table. @@ -844,7 +843,7 @@ func (s *fakeStmt) QueryContext(ctx context.Context, args []driver.NamedValue) ( return nil, driver.ErrBadConn } if s.c.isDirtyAndMark() { - return nil, errors.New("session is dirty") + return nil, errors.New("fakedb: session is dirty") } err := checkSubsetTypes(s.c.db.allowAny, args) @@ -900,7 +899,7 @@ func (s *fakeStmt) QueryContext(ctx context.Context, args []driver.NamedValue) ( idx := t.columnIndex(wcol.Column) if idx == -1 { t.mu.Unlock() - return nil, fmt.Errorf("db: invalid where clause column %q", wcol) + return nil, fmt.Errorf("fakedb: invalid where clause column %q", wcol) } tcol := trow.cols[idx] if bs, ok := tcol.([]byte); ok { @@ -1003,7 +1002,7 @@ type rowsCursor struct { err error // a clone of slices to give out to clients, indexed by the - // the original slice's first byte address. we clone them + // original slice's first byte address. we clone them // just so we're able to corrupt them on close. bytesClone map[*byte][]byte diff --git a/libgo/go/database/sql/sql.go b/libgo/go/database/sql/sql.go index 8f5588ed268..36179855db2 100644 --- a/libgo/go/database/sql/sql.go +++ b/libgo/go/database/sql/sql.go @@ -24,6 +24,7 @@ import ( "reflect" "runtime" "sort" + "strconv" "sync" "sync/atomic" "time" @@ -132,6 +133,31 @@ const ( LevelLinearizable ) +func (i IsolationLevel) String() string { + switch i { + 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(i)) + ")" + } +} + +var _ fmt.Stringer = LevelDefault + // TxOptions holds the transaction options to be used in DB.BeginTx. type TxOptions struct { // Isolation is the transaction isolation level. @@ -275,6 +301,10 @@ type Scanner interface { // // An error should be returned if the value cannot be stored // without loss of information. + // + // Reference types such as []byte are only valid until the next call to Scan + // and should not be retained. Their underlying memory is owned by the driver. + // If retention is necessary, copy their values before the next call to Scan. Scan(src interface{}) error } @@ -310,13 +340,17 @@ var ErrNoRows = errors.New("sql: no rows in result set") // // The sql package creates and frees connections automatically; it // also maintains a free pool of idle connections. If the database has -// a concept of per-connection state, such state can only be reliably -// observed within a transaction. Once DB.Begin is called, the +// a concept of per-connection state, such state can be reliably observed +// within a transaction (Tx) or connection (Conn). Once DB.Begin is called, the // returned Tx is bound to a single connection. Once Commit or // Rollback is called on the transaction, that transaction's // connection is returned to DB's idle connection pool. The pool size // can be controlled with SetMaxIdleConns. type DB struct { + // Atomic access only. At top of struct to prevent mis-alignment + // on 32-bit platforms. Of type time.Duration. + waitDuration int64 // Total time waited for new connections. + connector driver.Connector // numClosed is an atomic counter which represents a total number of // closed connections. Stmt.openStmt checks it before cleaning closed @@ -333,15 +367,18 @@ type DB struct { // maybeOpenNewConnections sends on the chan (one send per needed connection) // It is closed during db.Close(). The close tells the connectionOpener // goroutine to exit. - openerCh chan struct{} - resetterCh chan *driverConn - closed bool - dep map[finalCloser]depSet - lastPut map[*driverConn]string // stacktrace of last conn's put; debug only - maxIdle int // zero means defaultMaxIdleConns; negative means 0 - maxOpen int // <= 0 means unlimited - maxLifetime time.Duration // maximum amount of time a connection may be reused - cleanerCh chan struct{} + openerCh chan struct{} + resetterCh chan *driverConn + closed bool + dep map[finalCloser]depSet + lastPut map[*driverConn]string // stacktrace of last conn's put; debug only + maxIdle int // zero means defaultMaxIdleConns; negative means 0 + maxOpen int // <= 0 means unlimited + maxLifetime time.Duration // maximum amount of time a connection may be reused + cleanerCh chan struct{} + waitCount int64 // Total number of connections waited for. + maxIdleClosed int64 // Total number of connections closed due to idle. + maxLifetimeClosed int64 // Total number of connections closed due to max free limit. stop func() // stop cancels the connection opener and the session resetter. } @@ -503,7 +540,7 @@ type driverStmt struct { closeErr error // return value of previous Close call } -// Close ensures dirver.Stmt is only closed once any always returns the same +// Close ensures driver.Stmt is only closed once and always returns the same // result. func (ds *driverStmt) Close() error { ds.Lock() @@ -712,7 +749,9 @@ func (db *DB) Ping() error { return db.PingContext(context.Background()) } -// Close closes the database, releasing any open resources. +// Close closes the database and prevents new queries from starting. +// Close then waits for all queries that have started processing on the server +// to finish. // // It is rare to Close a DB, as the DB handle is meant to be // long-lived and shared between many goroutines. @@ -764,10 +803,13 @@ func (db *DB) maxIdleConnsLocked() int { // SetMaxIdleConns sets the maximum number of connections in the idle // connection pool. // -// If MaxOpenConns is greater than 0 but less than the new MaxIdleConns -// then the new MaxIdleConns will be reduced to match the MaxOpenConns limit +// If MaxOpenConns is greater than 0 but less than the new MaxIdleConns, +// then the new MaxIdleConns will be reduced to match the MaxOpenConns limit. // // If n <= 0, no idle connections are retained. +// +// The default max idle connections is currently 2. This may change in +// a future release. func (db *DB) SetMaxIdleConns(n int) { db.mu.Lock() if n > 0 { @@ -787,6 +829,7 @@ func (db *DB) SetMaxIdleConns(n int) { closing = db.freeConn[maxIdle:] db.freeConn = db.freeConn[:maxIdle] } + db.maxIdleClosed += int64(len(closing)) db.mu.Unlock() for _, c := range closing { c.Close() @@ -797,7 +840,7 @@ func (db *DB) SetMaxIdleConns(n int) { // // If MaxIdleConns is greater than 0 and the new MaxOpenConns is less than // MaxIdleConns, then MaxIdleConns will be reduced to match the new -// MaxOpenConns limit +// MaxOpenConns limit. // // If n <= 0, then there is no limit on the number of open connections. // The default is 0 (unlimited). @@ -879,6 +922,7 @@ func (db *DB) connectionCleaner(d time.Duration) { i-- } } + db.maxLifetimeClosed += int64(len(closing)) db.mu.Unlock() for _, c := range closing { @@ -894,17 +938,39 @@ func (db *DB) connectionCleaner(d time.Duration) { // DBStats contains database statistics. type DBStats struct { - // OpenConnections is the number of open connections to the database. - OpenConnections int + MaxOpenConnections int // Maximum number of open connections to the database. + + // Pool Status + OpenConnections int // The number of established connections both in use and idle. + InUse int // The number of connections currently in use. + Idle int // The number of idle connections. + + // Counters + 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. + MaxLifetimeClosed int64 // The total number of connections closed due to SetConnMaxLifetime. } // Stats returns database statistics. func (db *DB) Stats() DBStats { + wait := atomic.LoadInt64(&db.waitDuration) + db.mu.Lock() + defer db.mu.Unlock() + stats := DBStats{ + MaxOpenConnections: db.maxOpen, + + Idle: len(db.freeConn), OpenConnections: db.numOpen, + InUse: db.numOpen - len(db.freeConn), + + WaitCount: db.waitCount, + WaitDuration: time.Duration(wait), + MaxIdleClosed: db.maxIdleClosed, + MaxLifetimeClosed: db.maxLifetimeClosed, } - db.mu.Unlock() return stats } @@ -1057,8 +1123,11 @@ func (db *DB) conn(ctx context.Context, strategy connReuseStrategy) (*driverConn req := make(chan connRequest, 1) reqKey := db.nextRequestKeyLocked() db.connRequests[reqKey] = req + db.waitCount++ db.mu.Unlock() + waitStart := time.Now() + // Timeout the connection request with the context. select { case <-ctx.Done(): @@ -1067,15 +1136,20 @@ func (db *DB) conn(ctx context.Context, strategy connReuseStrategy) (*driverConn db.mu.Lock() delete(db.connRequests, reqKey) db.mu.Unlock() + + atomic.AddInt64(&db.waitDuration, int64(time.Since(waitStart))) + select { default: case ret, ok := <-req: - if ok { + if ok && ret.conn != nil { db.putConn(ret.conn, ret.err, false) } } return nil, ctx.Err() case ret, ok := <-req: + atomic.AddInt64(&db.waitDuration, int64(time.Since(waitStart))) + if !ok { return nil, errDBClosed } @@ -1250,6 +1324,7 @@ func (db *DB) putConnDBLocked(dc *driverConn, err error) bool { return true } else if err == nil && !db.closed && db.maxIdleConnsLocked() > len(db.freeConn) { db.freeConn = append(db.freeConn, dc) + db.maxIdleClosed++ db.startCleanerLocked() return true } @@ -1603,7 +1678,7 @@ func (db *DB) Driver() driver.Driver { // ErrConnDone is returned by any operation that is performed on a connection // that has already been returned to the connection pool. -var ErrConnDone = errors.New("database/sql: connection is already closed") +var ErrConnDone = errors.New("sql: connection is already closed") // Conn returns a single connection by either opening a new connection // or returning an existing connection from the connection pool. Conn will @@ -1851,7 +1926,7 @@ func (tx *Tx) isDone() bool { // ErrTxDone is returned by any operation that is performed on a transaction // that has already been committed or rolled back. -var ErrTxDone = errors.New("sql: Transaction has already been committed or rolled back") +var ErrTxDone = errors.New("sql: transaction has already been committed or rolled back") // close returns the connection to the pool and // must only be called by Tx.rollback or Tx.Commit. @@ -1914,7 +1989,7 @@ func (tx *Tx) closePrepared() { // Commit commits the transaction. func (tx *Tx) Commit() error { // Check context first to avoid transaction leak. - // If put it behind tx.done CompareAndSwap statement, we cant't ensure + // If put it behind tx.done CompareAndSwap statement, we can't ensure // the consistency between tx.done and the real COMMIT operation. select { default: @@ -2262,13 +2337,6 @@ func resultFromStatement(ctx context.Context, ci driver.Conn, ds *driverStmt, ar return nil, err } - // -1 means the driver doesn't know how to count the number of - // placeholders, so we won't sanity check input here and instead let the - // driver deal with errors. - if want := ds.si.NumInput(); want >= 0 && want != len(dargs) { - return nil, fmt.Errorf("sql: statement expects %d inputs; got %d", want, len(dargs)) - } - resi, err := ctxDriverStmtExec(ctx, ds.si, dargs) if err != nil { return nil, err @@ -2317,7 +2385,7 @@ func (s *Stmt) connStmt(ctx context.Context, strategy connReuseStrategy) (dc *dr } // In a transaction or connection, we always use the connection that the - // the stmt was created on. + // stmt was created on. if s.cg != nil { s.mu.Unlock() dc, releaseConn, err = s.cg.grabConn(ctx) // blocks, waiting for the connection. @@ -2434,24 +2502,11 @@ func (s *Stmt) Query(args ...interface{}) (*Rows, error) { func rowsiFromStatement(ctx context.Context, ci driver.Conn, ds *driverStmt, args ...interface{}) (driver.Rows, error) { ds.Lock() defer ds.Unlock() - dargs, err := driverArgsConnLocked(ci, ds, args) if err != nil { return nil, err } - - // -1 means the driver doesn't know how to count the number of - // placeholders, so we won't sanity check input here and instead let the - // driver deal with errors. - if want := ds.si.NumInput(); want >= 0 && want != len(dargs) { - return nil, fmt.Errorf("sql: statement expects %d inputs; got %d", want, len(dargs)) - } - - rowsi, err := ctxDriverStmtQuery(ctx, ds.si, dargs) - if err != nil { - return nil, err - } - return rowsi, nil + return ctxDriverStmtQuery(ctx, ds.si, dargs) } // QueryRowContext executes a prepared query statement with the given arguments. @@ -2460,11 +2515,6 @@ func rowsiFromStatement(ctx context.Context, ci driver.Conn, ds *driverStmt, arg // If the query selects no rows, the *Row's Scan will return ErrNoRows. // Otherwise, the *Row's Scan scans the first selected row and discards // the rest. -// -// Example usage: -// -// var name string -// err := nameByUseridStmt.QueryRowContext(ctx, id).Scan(&name) func (s *Stmt) QueryRowContext(ctx context.Context, args ...interface{}) *Row { rows, err := s.QueryContext(ctx, args...) if err != nil { @@ -2533,19 +2583,7 @@ func (s *Stmt) finalClose() error { } // Rows is the result of a query. Its cursor starts before the first row -// of the result set. Use Next to advance through the rows: -// -// rows, err := db.Query("SELECT ...") -// ... -// defer rows.Close() -// for rows.Next() { -// var id int -// var name string -// err = rows.Scan(&id, &name) -// ... -// } -// err = rows.Err() // get any error encountered during iteration -// ... +// of the result set. Use Next to advance from row to row. type Rows struct { dc *driverConn // owned; must call releaseConn when closed to release releaseConn func(error) @@ -2568,6 +2606,9 @@ type Rows struct { } func (rs *Rows) initContextClose(ctx, txctx context.Context) { + if ctx.Done() == nil && (txctx == nil || txctx.Done() == nil) { + return + } ctx, rs.cancel = context.WithCancel(ctx) go rs.awaitDone(ctx, txctx) } @@ -2861,7 +2902,7 @@ func rowsColumnInfoSetupConnLocked(rowsi driver.Rows) []*ColumnType { // // 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.Format3339Nano is used. +// the latter two, time.RFC3339Nano is used. // // Source values of type bool may be scanned into types *bool, // *interface{}, *string, *[]byte, or *RawBytes. @@ -2870,6 +2911,11 @@ func rowsColumnInfoSetupConnLocked(rowsi driver.Rows) []*ColumnType { // string inputs parseable by strconv.ParseBool. func (rs *Rows) Scan(dest ...interface{}) error { rs.closemu.RLock() + + if rs.lasterr != nil && rs.lasterr != io.EOF { + rs.closemu.RUnlock() + return rs.lasterr + } if rs.closed { rs.closemu.RUnlock() return errors.New("sql: Rows are closed") @@ -2885,7 +2931,7 @@ func (rs *Rows) Scan(dest ...interface{}) error { for i, sv := range rs.lastcols { err := convertAssign(dest[i], sv) if err != nil { - return fmt.Errorf("sql: Scan error on column index %d: %v", i, err) + return fmt.Errorf(`sql: Scan error on column index %d, name %q: %v`, i, rs.rowsi.Columns()[i], err) } } return nil @@ -2981,11 +3027,7 @@ func (r *Row) Scan(dest ...interface{}) error { return err } // Make sure the query can be processed to completion with no errors. - if err := r.rows.Close(); err != nil { - return err - } - - return nil + return r.rows.Close() } // A Result summarizes an executed SQL command. diff --git a/libgo/go/database/sql/sql_test.go b/libgo/go/database/sql/sql_test.go index ae6bf7102e3..f194744aefe 100644 --- a/libgo/go/database/sql/sql_test.go +++ b/libgo/go/database/sql/sql_test.go @@ -325,8 +325,8 @@ func TestQueryContext(t *testing.T) { } t.Fatalf("Scan: %v", err) } - if index == 2 && err == nil { - t.Fatal("expected an error on last scan") + if index == 2 && err != context.Canceled { + t.Fatalf("Scan: %v; want context.Canceled", err) } got = append(got, r) index++ @@ -1338,6 +1338,57 @@ func TestConnQuery(t *testing.T) { } } +func TestInvalidNilValues(t *testing.T) { + var date1 time.Time + var date2 int + + tests := []struct { + name string + input interface{} + expectedError string + }{ + { + name: "time.Time", + input: &date1, + expectedError: `sql: Scan error on column index 0, name "bdate": unsupported Scan, storing driver.Value type <nil> into type *time.Time`, + }, + { + name: "int", + input: &date2, + expectedError: `sql: Scan error on column index 0, name "bdate": converting driver.Value type <nil> ("<nil>") to a int: invalid syntax`, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + db := newTestDB(t, "people") + defer closeDB(t, db) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + conn, err := db.Conn(ctx) + if err != nil { + t.Fatal(err) + } + conn.dc.ci.(*fakeConn).skipDirtySession = true + defer conn.Close() + + err = conn.QueryRowContext(ctx, "SELECT|people|bdate|age=?", 1).Scan(tt.input) + if err == nil { + t.Fatal("expected error when querying nil column, but succeeded") + } + if err.Error() != tt.expectedError { + t.Fatalf("Expected error: %s\nReceived: %s", tt.expectedError, err.Error()) + } + + err = conn.PingContext(ctx) + if err != nil { + t.Fatal(err) + } + }) + } +} + func TestConnTx(t *testing.T) { db := newTestDB(t, "people") defer closeDB(t, db) @@ -1659,7 +1710,7 @@ func TestQueryRowNilScanDest(t *testing.T) { defer closeDB(t, db) var name *string // nil pointer err := db.QueryRow("SELECT|people|name|").Scan(name) - want := "sql: Scan error on column index 0: destination pointer is nil" + want := `sql: Scan error on column index 0, name "name": destination pointer is nil` if err == nil || err.Error() != want { t.Errorf("error = %q; want %q", err.Error(), want) } |