summaryrefslogtreecommitdiff
path: root/libgo/go/database
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@golang.org>2018-09-24 21:46:21 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2018-09-24 21:46:21 +0000
commitdd931d9b48647e898dc80927c532ae93cc09e192 (patch)
tree71be2295cd79b8a182f6130611658db8628772d5 /libgo/go/database
parent779d8a5ad09b01428726ea5a0e6c87bd9ac3c0e4 (diff)
downloadgcc-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.go12
-rw-r--r--libgo/go/database/sql/fakedb_test.go25
-rw-r--r--libgo/go/database/sql/sql.go182
-rw-r--r--libgo/go/database/sql/sql_test.go57
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)
}