summaryrefslogtreecommitdiff
path: root/src/pkg/database/sql
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/database/sql')
-rw-r--r--src/pkg/database/sql/convert.go83
-rw-r--r--src/pkg/database/sql/doc.txt2
-rw-r--r--src/pkg/database/sql/driver/driver.go32
-rw-r--r--src/pkg/database/sql/example_test.go45
-rw-r--r--src/pkg/database/sql/fakedb_test.go79
-rw-r--r--src/pkg/database/sql/sql.go408
-rw-r--r--src/pkg/database/sql/sql_test.go108
7 files changed, 594 insertions, 163 deletions
diff --git a/src/pkg/database/sql/convert.go b/src/pkg/database/sql/convert.go
index bfcb03ccf..853a7826c 100644
--- a/src/pkg/database/sql/convert.go
+++ b/src/pkg/database/sql/convert.go
@@ -14,53 +14,115 @@ import (
"strconv"
)
-// subsetTypeArgs takes a slice of arguments from callers of the sql
-// package and converts them into a slice of the driver package's
-// "subset types".
-func subsetTypeArgs(args []interface{}) ([]driver.Value, error) {
- out := make([]driver.Value, len(args))
+var errNilPtr = errors.New("destination pointer is nil") // embedded in descriptive error
+
+// driverArgs converts arguments from callers of Stmt.Exec and
+// Stmt.Query into driver Values.
+//
+// The statement si may be nil, if no statement is available.
+func driverArgs(si driver.Stmt, args []interface{}) ([]driver.Value, error) {
+ dargs := make([]driver.Value, len(args))
+ cc, ok := si.(driver.ColumnConverter)
+
+ // Normal path, for a driver.Stmt that is not a ColumnConverter.
+ if !ok {
+ for n, arg := range args {
+ var err error
+ dargs[n], err = driver.DefaultParameterConverter.ConvertValue(arg)
+ if err != nil {
+ return nil, fmt.Errorf("sql: converting Exec argument #%d's type: %v", n, err)
+ }
+ }
+ return dargs, nil
+ }
+
+ // Let the Stmt convert its own arguments.
for n, arg := range args {
+ // First, see if the value itself knows how to convert
+ // itself to a driver type. For example, a NullString
+ // struct changing into a string or nil.
+ if svi, ok := arg.(driver.Valuer); ok {
+ sv, err := svi.Value()
+ if err != nil {
+ return nil, fmt.Errorf("sql: argument index %d from Value: %v", n, err)
+ }
+ if !driver.IsValue(sv) {
+ return nil, fmt.Errorf("sql: argument index %d: non-subset type %T returned from Value", n, sv)
+ }
+ arg = sv
+ }
+
+ // Second, ask the column to sanity check itself. For
+ // example, drivers might use this to make sure that
+ // an int64 values being inserted into a 16-bit
+ // integer field is in range (before getting
+ // truncated), or that a nil can't go into a NOT NULL
+ // column before going across the network to get the
+ // same error.
var err error
- out[n], err = driver.DefaultParameterConverter.ConvertValue(arg)
+ dargs[n], err = cc.ColumnConverter(n).ConvertValue(arg)
if err != nil {
- return nil, fmt.Errorf("sql: converting argument #%d's type: %v", n+1, err)
+ return nil, fmt.Errorf("sql: converting argument #%d's type: %v", n, err)
+ }
+ if !driver.IsValue(dargs[n]) {
+ return nil, fmt.Errorf("sql: driver ColumnConverter error converted %T to unsupported type %T",
+ arg, dargs[n])
}
}
- return out, nil
+
+ return dargs, nil
}
// convertAssign copies to dest the value in src, converting it if possible.
// An error is returned if the copy would result in loss of information.
// dest should be a pointer type.
func convertAssign(dest, src interface{}) error {
- // Common cases, without reflect. Fall through.
+ // Common cases, without reflect.
switch s := src.(type) {
case string:
switch d := dest.(type) {
case *string:
+ if d == nil {
+ return errNilPtr
+ }
*d = s
return nil
case *[]byte:
+ if d == nil {
+ return errNilPtr
+ }
*d = []byte(s)
return nil
}
case []byte:
switch d := dest.(type) {
case *string:
+ if d == nil {
+ return errNilPtr
+ }
*d = string(s)
return nil
case *interface{}:
+ if d == nil {
+ return errNilPtr
+ }
bcopy := make([]byte, len(s))
copy(bcopy, s)
*d = bcopy
return nil
case *[]byte:
+ if d == nil {
+ return errNilPtr
+ }
*d = s
return nil
}
case nil:
switch d := dest.(type) {
case *[]byte:
+ if d == nil {
+ return errNilPtr
+ }
*d = nil
return nil
}
@@ -98,6 +160,9 @@ func convertAssign(dest, src interface{}) error {
if dpv.Kind() != reflect.Ptr {
return errors.New("destination not a pointer")
}
+ if dpv.IsNil() {
+ return errNilPtr
+ }
if !sv.IsValid() {
sv = reflect.ValueOf(src)
diff --git a/src/pkg/database/sql/doc.txt b/src/pkg/database/sql/doc.txt
index fb1659548..405c5ed2a 100644
--- a/src/pkg/database/sql/doc.txt
+++ b/src/pkg/database/sql/doc.txt
@@ -21,7 +21,7 @@ Goals of the sql and sql/driver packages:
Database Driver -> sql (to register) + sql/driver (implement interfaces)
* Make type casting/conversions consistent between all drivers. To
- achieve this, most of the conversions are done in the db package,
+ achieve this, most of the conversions are done in the sql package,
not in each driver. The drivers then only have to deal with a
smaller set of types.
diff --git a/src/pkg/database/sql/driver/driver.go b/src/pkg/database/sql/driver/driver.go
index 2f5280db8..2434e419b 100644
--- a/src/pkg/database/sql/driver/driver.go
+++ b/src/pkg/database/sql/driver/driver.go
@@ -56,7 +56,7 @@ var ErrBadConn = errors.New("driver: bad connection")
// Execer is an optional interface that may be implemented by a Conn.
//
-// If a Conn does not implement Execer, the db package's DB.Exec will
+// If a Conn does not implement Execer, the sql package's DB.Exec will
// first prepare a query, execute the statement, and then close the
// statement.
//
@@ -65,6 +65,17 @@ type Execer interface {
Exec(query string, args []Value) (Result, error)
}
+// Queryer is an optional interface that may be implemented by a Conn.
+//
+// If a Conn does not implement Queryer, the sql package's DB.Query will
+// first prepare a query, execute the statement, and then close the
+// statement.
+//
+// Query may return ErrSkip.
+type Queryer interface {
+ Query(query string, args []Value) (Rows, error)
+}
+
// Conn is a connection to a database. It is not used concurrently
// by multiple goroutines.
//
@@ -104,23 +115,8 @@ type Result interface {
type Stmt interface {
// Close closes the statement.
//
- // Closing a statement should not interrupt any outstanding
- // query created from that statement. That is, the following
- // order of operations is valid:
- //
- // * create a driver statement
- // * call Query on statement, returning Rows
- // * close the statement
- // * read from Rows
- //
- // If closing a statement invalidates currently-running
- // queries, the final step above will incorrectly fail.
- //
- // TODO(bradfitz): possibly remove the restriction above, if
- // enough driver authors object and find it complicates their
- // code too much. The sql package could be smarter about
- // refcounting the statement and closing it at the appropriate
- // time.
+ // As of Go 1.1, a Stmt will not be closed if it's in use
+ // by any queries.
Close() error
// NumInput returns the number of placeholder parameters.
diff --git a/src/pkg/database/sql/example_test.go b/src/pkg/database/sql/example_test.go
new file mode 100644
index 000000000..d47eed50c
--- /dev/null
+++ b/src/pkg/database/sql/example_test.go
@@ -0,0 +1,45 @@
+// Copyright 2013 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_test
+
+import (
+ "database/sql"
+ "fmt"
+ "log"
+)
+
+var db *sql.DB
+
+func ExampleDB_Query() {
+ age := 27
+ rows, err := db.Query("SELECT name FROM users WHERE age=?", age)
+ if err != nil {
+ log.Fatal(err)
+ }
+ for rows.Next() {
+ var name string
+ if err := rows.Scan(&name); err != nil {
+ log.Fatal(err)
+ }
+ fmt.Printf("%s is %d\n", name, age)
+ }
+ if err := rows.Err(); err != nil {
+ log.Fatal(err)
+ }
+}
+
+func ExampleDB_QueryRow() {
+ id := 123
+ var username string
+ err := db.QueryRow("SELECT username FROM users WHERE id=?", id).Scan(&username)
+ switch {
+ case err == sql.ErrNoRows:
+ log.Printf("No user with that ID.")
+ case err != nil:
+ log.Fatal(err)
+ default:
+ fmt.Printf("Username is %s\n", username)
+ }
+}
diff --git a/src/pkg/database/sql/fakedb_test.go b/src/pkg/database/sql/fakedb_test.go
index 184e7756c..55597f7de 100644
--- a/src/pkg/database/sql/fakedb_test.go
+++ b/src/pkg/database/sql/fakedb_test.go
@@ -31,7 +31,7 @@ var _ = log.Printf
// INSERT|<tablename>|col=val,col2=val2,col3=?
// SELECT|<tablename>|projectcol1,projectcol2|filtercol=?,filtercol2=?
//
-// When opening a a fakeDriver's database, it starts empty with no
+// When opening a fakeDriver's database, it starts empty with no
// tables. All tables and data are stored in memory only.
type fakeDriver struct {
mu sync.Mutex
@@ -42,9 +42,10 @@ type fakeDriver struct {
type fakeDB struct {
name string
- mu sync.Mutex
- free []*fakeConn
- tables map[string]*table
+ mu sync.Mutex
+ free []*fakeConn
+ tables map[string]*table
+ badConn bool
}
type table struct {
@@ -83,6 +84,7 @@ type fakeConn struct {
stmtsMade int
stmtsClosed int
numPrepare int
+ bad bool
}
func (c *fakeConn) incrStat(v *int) {
@@ -122,7 +124,9 @@ func init() {
// Supports dsn forms:
// <dbname>
-// <dbname>;<opts> (no currently supported options)
+// <dbname>;<opts> (only currently supported option is `badConn`,
+// which causes driver.ErrBadConn to be returned on
+// every other conn.Begin())
func (d *fakeDriver) Open(dsn string) (driver.Conn, error) {
parts := strings.Split(dsn, ";")
if len(parts) < 1 {
@@ -135,7 +139,12 @@ func (d *fakeDriver) Open(dsn string) (driver.Conn, error) {
d.mu.Lock()
d.openCount++
d.mu.Unlock()
- return &fakeConn{db: db}, nil
+ conn := &fakeConn{db: db}
+
+ if len(parts) >= 2 && parts[1] == "badConn" {
+ conn.bad = true
+ }
+ return conn, nil
}
func (d *fakeDriver) getDB(name string) *fakeDB {
@@ -199,7 +208,20 @@ func (db *fakeDB) columnType(table, column string) (typ string, ok bool) {
return "", false
}
+func (c *fakeConn) isBad() bool {
+ // if not simulating bad conn, do nothing
+ if !c.bad {
+ return false
+ }
+ // alternate between bad conn and not bad conn
+ c.db.badConn = !c.db.badConn
+ return c.db.badConn
+}
+
func (c *fakeConn) Begin() (driver.Tx, error) {
+ if c.isBad() {
+ return nil, driver.ErrBadConn
+ }
if c.currTx != nil {
return nil, errors.New("already in a transaction")
}
@@ -234,7 +256,19 @@ func checkSubsetTypes(args []driver.Value) error {
func (c *fakeConn) Exec(query string, args []driver.Value) (driver.Result, error) {
// This is an optional interface, but it's implemented here
- // just to check that all the args of of the proper types.
+ // just to check that all the args are of the proper types.
+ // ErrSkip is returned so the caller acts as if we didn't
+ // implement this at all.
+ err := checkSubsetTypes(args)
+ if err != nil {
+ return nil, err
+ }
+ return nil, driver.ErrSkip
+}
+
+func (c *fakeConn) Query(query string, args []driver.Value) (driver.Rows, error) {
+ // This is an optional interface, but it's implemented here
+ // just to check that all the args are of the proper types.
// ErrSkip is returned so the caller acts as if we didn't
// implement this at all.
err := checkSubsetTypes(args)
@@ -249,7 +283,7 @@ func errf(msg string, args ...interface{}) error {
}
// parts are table|selectCol1,selectCol2|whereCol=?,whereCol2=?
-// (note that where where columns must always contain ? marks,
+// (note that where columns must always contain ? marks,
// just a limitation for fakedb)
func (c *fakeConn) prepareSelect(stmt *fakeStmt, parts []string) (driver.Stmt, error) {
if len(parts) != 3 {
@@ -383,6 +417,9 @@ func (c *fakeConn) Prepare(query string) (driver.Stmt, error) {
}
func (s *fakeStmt) ColumnConverter(idx int) driver.ValueConverter {
+ if len(s.placeholderConverter) == 0 {
+ return driver.DefaultParameterConverter
+ }
return s.placeholderConverter[idx]
}
@@ -598,6 +635,28 @@ func (rc *rowsCursor) Next(dest []driver.Value) error {
return nil
}
+// fakeDriverString is like driver.String, but indirects pointers like
+// DefaultValueConverter.
+//
+// This could be surprising behavior to retroactively apply to
+// driver.String now that Go1 is out, but this is convenient for
+// our TestPointerParamsAndScans.
+//
+type fakeDriverString struct{}
+
+func (fakeDriverString) ConvertValue(v interface{}) (driver.Value, error) {
+ switch c := v.(type) {
+ case string, []byte:
+ return v, nil
+ case *string:
+ if c == nil {
+ return nil, nil
+ }
+ return *c, nil
+ }
+ return fmt.Sprintf("%v", v), nil
+}
+
func converterForType(typ string) driver.ValueConverter {
switch typ {
case "bool":
@@ -607,9 +666,9 @@ func converterForType(typ string) driver.ValueConverter {
case "int32":
return driver.Int32
case "string":
- return driver.NotNull{Converter: driver.String}
+ return driver.NotNull{Converter: fakeDriverString{}}
case "nullstring":
- return driver.Null{Converter: driver.String}
+ return driver.Null{Converter: fakeDriverString{}}
case "int64":
// TODO(coopernurse): add type-specific converter
return driver.NotNull{Converter: driver.DefaultParameterConverter}
diff --git a/src/pkg/database/sql/sql.go b/src/pkg/database/sql/sql.go
index 89136ef6e..4faaa11b1 100644
--- a/src/pkg/database/sql/sql.go
+++ b/src/pkg/database/sql/sql.go
@@ -11,6 +11,7 @@ import (
"errors"
"fmt"
"io"
+ "runtime"
"sync"
)
@@ -189,9 +190,66 @@ type DB struct {
driver driver.Driver
dsn string
- mu sync.Mutex // protects freeConn and closed
- freeConn []driver.Conn
- closed bool
+ mu sync.Mutex // protects following fields
+ outConn map[driver.Conn]bool // whether the conn is in use
+ freeConn []driver.Conn
+ closed bool
+ dep map[finalCloser]depSet
+ onConnPut map[driver.Conn][]func() // code (with mu held) run when conn is next returned
+ lastPut map[driver.Conn]string // stacktrace of last conn's put; debug only
+}
+
+// depSet is a finalCloser's outstanding dependencies
+type depSet map[interface{}]bool // set of true bools
+
+// The finalCloser interface is used by (*DB).addDep and (*DB).get
+type finalCloser interface {
+ // finalClose is called when the reference count of an object
+ // goes to zero. (*DB).mu is not held while calling it.
+ finalClose() error
+}
+
+// addDep notes that x now depends on dep, and x's finalClose won't be
+// called until all of x's dependencies are removed with removeDep.
+func (db *DB) addDep(x finalCloser, dep interface{}) {
+ //println(fmt.Sprintf("addDep(%T %p, %T %p)", x, x, dep, dep))
+ db.mu.Lock()
+ defer db.mu.Unlock()
+ if db.dep == nil {
+ db.dep = make(map[finalCloser]depSet)
+ }
+ xdep := db.dep[x]
+ if xdep == nil {
+ xdep = make(depSet)
+ db.dep[x] = xdep
+ }
+ xdep[dep] = true
+}
+
+// removeDep notes that x no longer depends on dep.
+// If x still has dependencies, nil is returned.
+// If x no longer has any dependencies, its finalClose method will be
+// called and its error value will be returned.
+func (db *DB) removeDep(x finalCloser, dep interface{}) error {
+ //println(fmt.Sprintf("removeDep(%T %p, %T %p)", x, x, dep, dep))
+ done := false
+
+ db.mu.Lock()
+ xdep := db.dep[x]
+ if xdep != nil {
+ delete(xdep, dep)
+ if len(xdep) == 0 {
+ delete(db.dep, x)
+ done = true
+ }
+ }
+ db.mu.Unlock()
+
+ if !done {
+ return nil
+ }
+ //println(fmt.Sprintf("calling final close on %T %v (%#v)", x, x, x))
+ return x.finalClose()
}
// Open opens a database specified by its database driver name and a
@@ -201,11 +259,20 @@ type DB struct {
// Most users will open a database via a driver-specific connection
// helper function that returns a *DB.
func Open(driverName, dataSourceName string) (*DB, error) {
- driver, ok := drivers[driverName]
+ driveri, ok := drivers[driverName]
if !ok {
return nil, fmt.Errorf("sql: unknown driver %q (forgotten import?)", driverName)
}
- return &DB{driver: driver, dsn: dataSourceName}, nil
+ // TODO: optionally proactively connect to a Conn to check
+ // the dataSourceName: golang.org/issue/4804
+ db := &DB{
+ driver: driveri,
+ dsn: dataSourceName,
+ outConn: make(map[driver.Conn]bool),
+ lastPut: make(map[driver.Conn]string),
+ onConnPut: make(map[driver.Conn][]func()),
+ }
+ return db, nil
}
// Close closes the database, releasing any open resources.
@@ -241,22 +308,38 @@ func (db *DB) conn() (driver.Conn, error) {
if n := len(db.freeConn); n > 0 {
conn := db.freeConn[n-1]
db.freeConn = db.freeConn[:n-1]
+ db.outConn[conn] = true
db.mu.Unlock()
return conn, nil
}
db.mu.Unlock()
- return db.driver.Open(db.dsn)
+ conn, err := db.driver.Open(db.dsn)
+ if err == nil {
+ db.mu.Lock()
+ db.outConn[conn] = true
+ db.mu.Unlock()
+ }
+ return conn, err
}
+// connIfFree returns (wanted, true) if wanted is still a valid conn and
+// isn't in use.
+//
+// If wanted is valid but in use, connIfFree returns (wanted, false).
+// If wanted is invalid, connIfFre returns (nil, false).
func (db *DB) connIfFree(wanted driver.Conn) (conn driver.Conn, ok bool) {
db.mu.Lock()
defer db.mu.Unlock()
+ if db.outConn[wanted] {
+ return conn, false
+ }
for i, conn := range db.freeConn {
if conn != wanted {
continue
}
db.freeConn[i] = db.freeConn[len(db.freeConn)-1]
db.freeConn = db.freeConn[:len(db.freeConn)-1]
+ db.outConn[wanted] = true
return wanted, true
}
return nil, false
@@ -265,14 +348,52 @@ func (db *DB) connIfFree(wanted driver.Conn) (conn driver.Conn, ok bool) {
// putConnHook is a hook for testing.
var putConnHook func(*DB, driver.Conn)
+// noteUnusedDriverStatement notes that si is no longer used and should
+// be closed whenever possible (when c is next not in use), unless c is
+// already closed.
+func (db *DB) noteUnusedDriverStatement(c driver.Conn, si driver.Stmt) {
+ db.mu.Lock()
+ defer db.mu.Unlock()
+ if db.outConn[c] {
+ db.onConnPut[c] = append(db.onConnPut[c], func() {
+ si.Close()
+ })
+ } else {
+ si.Close()
+ }
+}
+
+// debugGetPut determines whether getConn & putConn calls' stack traces
+// are returned for more verbose crashes.
+const debugGetPut = false
+
// putConn adds a connection to the db's free pool.
-// err is optionally the last error that occured on this connection.
+// err is optionally the last error that occurred on this connection.
func (db *DB) putConn(c driver.Conn, err error) {
+ db.mu.Lock()
+ if !db.outConn[c] {
+ if debugGetPut {
+ fmt.Printf("putConn(%v) DUPLICATE was: %s\n\nPREVIOUS was: %s", c, stack(), db.lastPut[c])
+ }
+ panic("sql: connection returned that was never out")
+ }
+ if debugGetPut {
+ db.lastPut[c] = stack()
+ }
+ delete(db.outConn, c)
+
+ if fns, ok := db.onConnPut[c]; ok {
+ for _, fn := range fns {
+ fn()
+ }
+ delete(db.onConnPut, c)
+ }
+
if err == driver.ErrBadConn {
// Don't reuse bad connections.
+ db.mu.Unlock()
return
}
- db.mu.Lock()
if putConnHook != nil {
putConnHook(db, c)
}
@@ -287,7 +408,9 @@ func (db *DB) putConn(c driver.Conn, err error) {
c.Close()
}
-// Prepare creates a prepared statement for later execution.
+// Prepare creates a prepared statement for later queries or executions.
+// Multiple queries or executions may be run concurrently from the
+// returned statement.
func (db *DB) Prepare(query string) (*Stmt, error) {
var stmt *Stmt
var err error
@@ -300,7 +423,7 @@ func (db *DB) Prepare(query string) (*Stmt, error) {
return stmt, err
}
-func (db *DB) prepare(query string) (stmt *Stmt, err error) {
+func (db *DB) prepare(query string) (*Stmt, error) {
// TODO: check if db.driver supports an optional
// driver.Preparer interface and call that instead, if so,
// otherwise we make a prepared statement that's bound
@@ -311,28 +434,27 @@ func (db *DB) prepare(query string) (stmt *Stmt, err error) {
if err != nil {
return nil, err
}
- defer db.putConn(ci, err)
si, err := ci.Prepare(query)
if err != nil {
+ db.putConn(ci, err)
return nil, err
}
- stmt = &Stmt{
+ stmt := &Stmt{
db: db,
query: query,
css: []connStmt{{ci, si}},
}
+ db.addDep(stmt, stmt)
return stmt, nil
}
// Exec executes a query without returning any rows.
+// The args are for any placeholder parameters in the query.
func (db *DB) Exec(query string, args ...interface{}) (Result, error) {
- sargs, err := subsetTypeArgs(args)
- if err != nil {
- return nil, err
- }
var res Result
+ var err error
for i := 0; i < 10; i++ {
- res, err = db.exec(query, sargs)
+ res, err = db.exec(query, args)
if err != driver.ErrBadConn {
break
}
@@ -340,15 +462,21 @@ func (db *DB) Exec(query string, args ...interface{}) (Result, error) {
return res, err
}
-func (db *DB) exec(query string, sargs []driver.Value) (res Result, err error) {
+func (db *DB) exec(query string, args []interface{}) (res Result, err error) {
ci, err := db.conn()
if err != nil {
return nil, err
}
- defer db.putConn(ci, err)
+ defer func() {
+ db.putConn(ci, err)
+ }()
if execer, ok := ci.(driver.Execer); ok {
- resi, err := execer.Exec(query, sargs)
+ dargs, err := driverArgs(nil, args)
+ if err != nil {
+ return nil, err
+ }
+ resi, err := execer.Exec(query, dargs)
if err != driver.ErrSkip {
if err != nil {
return nil, err
@@ -363,25 +491,83 @@ func (db *DB) exec(query string, sargs []driver.Value) (res Result, err error) {
}
defer sti.Close()
- resi, err := sti.Exec(sargs)
+ return resultFromStatement(sti, args...)
+}
+
+// Query executes a query that returns rows, typically a SELECT.
+// The args are for any placeholder parameters in the query.
+func (db *DB) Query(query string, args ...interface{}) (*Rows, error) {
+ var rows *Rows
+ var err error
+ for i := 0; i < 10; i++ {
+ rows, err = db.query(query, args)
+ if err != driver.ErrBadConn {
+ break
+ }
+ }
+ return rows, err
+}
+
+func (db *DB) query(query string, args []interface{}) (*Rows, error) {
+ ci, err := db.conn()
if err != nil {
return nil, err
}
- return result{resi}, nil
+
+ releaseConn := func(err error) { db.putConn(ci, err) }
+
+ return db.queryConn(ci, releaseConn, query, args)
}
-// Query executes a query that returns rows, typically a SELECT.
-func (db *DB) Query(query string, args ...interface{}) (*Rows, error) {
- stmt, err := db.Prepare(query)
+// queryConn executes a query on the given connection.
+// The connection gets released by the releaseConn function.
+func (db *DB) queryConn(ci driver.Conn, releaseConn func(error), query string, args []interface{}) (*Rows, error) {
+ if queryer, ok := ci.(driver.Queryer); ok {
+ dargs, err := driverArgs(nil, args)
+ if err != nil {
+ releaseConn(err)
+ return nil, err
+ }
+ rowsi, err := queryer.Query(query, dargs)
+ if err != driver.ErrSkip {
+ if err != nil {
+ releaseConn(err)
+ return nil, err
+ }
+ // Note: ownership of ci passes to the *Rows, to be freed
+ // with releaseConn.
+ rows := &Rows{
+ db: db,
+ ci: ci,
+ releaseConn: releaseConn,
+ rowsi: rowsi,
+ }
+ return rows, nil
+ }
+ }
+
+ sti, err := ci.Prepare(query)
if err != nil {
+ releaseConn(err)
return nil, err
}
- rows, err := stmt.Query(args...)
+
+ rowsi, err := rowsiFromStatement(sti, args...)
if err != nil {
- stmt.Close()
+ releaseConn(err)
+ sti.Close()
return nil, err
}
- rows.closeStmt = stmt
+
+ // Note: ownership of ci passes to the *Rows, to be freed
+ // with releaseConn.
+ rows := &Rows{
+ db: db,
+ ci: ci,
+ releaseConn: releaseConn,
+ rowsi: rowsi,
+ closeStmt: sti,
+ }
return rows, nil
}
@@ -415,7 +601,7 @@ func (db *DB) begin() (tx *Tx, err error) {
txi, err := ci.Begin()
if err != nil {
db.putConn(ci, err)
- return nil, fmt.Errorf("sql: failed to Begin transaction: %v", err)
+ return nil, err
}
return &Tx{
db: db,
@@ -577,13 +763,12 @@ func (tx *Tx) Exec(query string, args ...interface{}) (Result, error) {
}
defer tx.releaseConn()
- sargs, err := subsetTypeArgs(args)
- if err != nil {
- return nil, err
- }
-
if execer, ok := ci.(driver.Execer); ok {
- resi, err := execer.Exec(query, sargs)
+ dargs, err := driverArgs(nil, args)
+ if err != nil {
+ return nil, err
+ }
+ resi, err := execer.Exec(query, dargs)
if err == nil {
return result{resi}, nil
}
@@ -598,29 +783,19 @@ func (tx *Tx) Exec(query string, args ...interface{}) (Result, error) {
}
defer sti.Close()
- resi, err := sti.Exec(sargs)
- if err != nil {
- return nil, err
- }
- return result{resi}, nil
+ return resultFromStatement(sti, args...)
}
// Query executes a query that returns rows, typically a SELECT.
func (tx *Tx) Query(query string, args ...interface{}) (*Rows, error) {
- if tx.done {
- return nil, ErrTxDone
- }
- stmt, err := tx.Prepare(query)
- if err != nil {
- return nil, err
- }
- rows, err := stmt.Query(args...)
+ ci, err := tx.grabConn()
if err != nil {
- stmt.Close()
return nil, err
}
- rows.closeStmt = stmt
- return rows, err
+
+ releaseConn := func(err error) { tx.releaseConn() }
+
+ return tx.db.queryConn(ci, releaseConn, query, args)
}
// QueryRow executes a query that is expected to return at most one row.
@@ -644,6 +819,8 @@ type Stmt struct {
query string // that created the Stmt
stickyErr error // if non-nil, this error is returned for all operations
+ closemu sync.RWMutex // held exclusively during close, for read otherwise.
+
// If in a transaction, else both nil:
tx *Tx
txsi driver.Stmt
@@ -661,12 +838,18 @@ type Stmt struct {
// Exec executes a prepared statement with the given arguments and
// returns a Result summarizing the effect of the statement.
func (s *Stmt) Exec(args ...interface{}) (Result, error) {
+ s.closemu.RLock()
+ defer s.closemu.RUnlock()
_, releaseConn, si, err := s.connStmt()
if err != nil {
return nil, err
}
defer releaseConn(nil)
+ return resultFromStatement(si, args...)
+}
+
+func resultFromStatement(si driver.Stmt, args ...interface{}) (Result, error) {
// -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.
@@ -674,51 +857,12 @@ func (s *Stmt) Exec(args ...interface{}) (Result, error) {
return nil, fmt.Errorf("sql: expected %d arguments, got %d", want, len(args))
}
- sargs := make([]driver.Value, len(args))
-
- // Convert args to subset types.
- if cc, ok := si.(driver.ColumnConverter); ok {
- for n, arg := range args {
- // First, see if the value itself knows how to convert
- // itself to a driver type. For example, a NullString
- // struct changing into a string or nil.
- if svi, ok := arg.(driver.Valuer); ok {
- sv, err := svi.Value()
- if err != nil {
- return nil, fmt.Errorf("sql: argument index %d from Value: %v", n, err)
- }
- if !driver.IsValue(sv) {
- return nil, fmt.Errorf("sql: argument index %d: non-subset type %T returned from Value", n, sv)
- }
- arg = sv
- }
-
- // Second, ask the column to sanity check itself. For
- // example, drivers might use this to make sure that
- // an int64 values being inserted into a 16-bit
- // integer field is in range (before getting
- // truncated), or that a nil can't go into a NOT NULL
- // column before going across the network to get the
- // same error.
- sargs[n], err = cc.ColumnConverter(n).ConvertValue(arg)
- if err != nil {
- return nil, fmt.Errorf("sql: converting Exec argument #%d's type: %v", n, err)
- }
- if !driver.IsValue(sargs[n]) {
- return nil, fmt.Errorf("sql: driver ColumnConverter error converted %T to unsupported type %T",
- arg, sargs[n])
- }
- }
- } else {
- for n, arg := range args {
- sargs[n], err = driver.DefaultParameterConverter.ConvertValue(arg)
- if err != nil {
- return nil, fmt.Errorf("sql: converting Exec argument #%d's type: %v", n, err)
- }
- }
+ dargs, err := driverArgs(si, args)
+ if err != nil {
+ return nil, err
}
- resi, err := si.Exec(sargs)
+ resi, err := si.Exec(dargs)
if err != nil {
return nil, err
}
@@ -794,35 +938,54 @@ func (s *Stmt) connStmt() (ci driver.Conn, releaseConn func(error), si driver.St
// Query executes a prepared query statement with the given arguments
// and returns the query results as a *Rows.
func (s *Stmt) Query(args ...interface{}) (*Rows, error) {
+ s.closemu.RLock()
+ defer s.closemu.RUnlock()
+
ci, releaseConn, si, err := s.connStmt()
if err != nil {
return nil, err
}
+ rowsi, err := rowsiFromStatement(si, args...)
+ if err != nil {
+ releaseConn(err)
+ return nil, err
+ }
+
+ // Note: ownership of ci passes to the *Rows, to be freed
+ // with releaseConn.
+ rows := &Rows{
+ db: s.db,
+ ci: ci,
+ rowsi: rowsi,
+ // releaseConn set below
+ }
+ s.db.addDep(s, rows)
+ rows.releaseConn = func(err error) {
+ releaseConn(err)
+ s.db.removeDep(s, rows)
+ }
+ return rows, nil
+}
+
+func rowsiFromStatement(si driver.Stmt, args ...interface{}) (driver.Rows, error) {
// -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 := si.NumInput(); want != -1 && len(args) != want {
return nil, fmt.Errorf("sql: statement expects %d inputs; got %d", si.NumInput(), len(args))
}
- sargs, err := subsetTypeArgs(args)
+
+ dargs, err := driverArgs(si, args)
if err != nil {
return nil, err
}
- rowsi, err := si.Query(sargs)
+
+ rowsi, err := si.Query(dargs)
if err != nil {
- releaseConn(err)
return nil, err
}
- // Note: ownership of ci passes to the *Rows, to be freed
- // with releaseConn.
- rows := &Rows{
- db: s.db,
- ci: ci,
- releaseConn: releaseConn,
- rowsi: rowsi,
- }
- return rows, nil
+ return rowsi, nil
}
// QueryRow executes a prepared query statement with the given arguments.
@@ -846,6 +1009,9 @@ func (s *Stmt) QueryRow(args ...interface{}) *Row {
// Close closes the statement.
func (s *Stmt) Close() error {
+ s.closemu.Lock()
+ defer s.closemu.Unlock()
+
if s.stickyErr != nil {
return s.stickyErr
}
@@ -858,18 +1024,17 @@ func (s *Stmt) Close() error {
if s.tx != nil {
s.txsi.Close()
- } else {
- for _, v := range s.css {
- if ci, match := s.db.connIfFree(v.ci); match {
- v.si.Close()
- s.db.putConn(ci, nil)
- } else {
- // TODO(bradfitz): care that we can't close
- // this statement because the statement's
- // connection is in use?
- }
- }
+ return nil
+ }
+
+ return s.db.removeDep(s, s)
+}
+
+func (s *Stmt) finalClose() error {
+ for _, v := range s.css {
+ s.db.noteUnusedDriverStatement(v.ci, v.si)
}
+ s.css = nil
return nil
}
@@ -888,14 +1053,14 @@ func (s *Stmt) Close() error {
// ...
type Rows struct {
db *DB
- ci driver.Conn // owned; must call putconn when closed to release
+ ci driver.Conn // owned; must call releaseConn when closed to release
releaseConn func(error)
rowsi driver.Rows
closed bool
lastcols []driver.Value
lasterr error
- closeStmt *Stmt // if non-nil, statement to Close on close
+ closeStmt driver.Stmt // if non-nil, statement to Close on close
}
// Next prepares the next result row for reading with the Scan method.
@@ -1064,3 +1229,8 @@ type Result interface {
type result struct {
driver.Result
}
+
+func stack() string {
+ var buf [1024]byte
+ return string(buf[:runtime.Stack(buf[:], false)])
+}
diff --git a/src/pkg/database/sql/sql_test.go b/src/pkg/database/sql/sql_test.go
index b29670586..53b229600 100644
--- a/src/pkg/database/sql/sql_test.go
+++ b/src/pkg/database/sql/sql_test.go
@@ -8,7 +8,6 @@ import (
"database/sql/driver"
"fmt"
"reflect"
- "runtime"
"strings"
"testing"
"time"
@@ -63,6 +62,10 @@ func exec(t *testing.T, db *DB, query string, args ...interface{}) {
}
func closeDB(t *testing.T, db *DB) {
+ if e := recover(); e != nil {
+ fmt.Printf("Panic: %v\n", e)
+ panic(e)
+ }
err := db.Close()
if err != nil {
t.Fatalf("error closing DB: %v", err)
@@ -270,6 +273,35 @@ func TestStatementQueryRow(t *testing.T) {
}
+// golang.org/issue/3734
+func TestStatementQueryRowConcurrent(t *testing.T) {
+ db := newTestDB(t, "people")
+ defer closeDB(t, db)
+ stmt, err := db.Prepare("SELECT|people|age|name=?")
+ if err != nil {
+ t.Fatalf("Prepare: %v", err)
+ }
+ defer stmt.Close()
+
+ const n = 10
+ ch := make(chan error, n)
+ for i := 0; i < n; i++ {
+ go func() {
+ var age int
+ err := stmt.QueryRow("Alice").Scan(&age)
+ if err == nil && age != 1 {
+ err = fmt.Errorf("unexpected age %d", age)
+ }
+ ch <- err
+ }()
+ }
+ for i := 0; i < n; i++ {
+ if err := <-ch; err != nil {
+ t.Error(err)
+ }
+ }
+}
+
// just a test of fakedb itself
func TestBogusPreboundParameters(t *testing.T) {
db := newTestDB(t, "foo")
@@ -306,8 +338,8 @@ func TestExec(t *testing.T) {
{[]interface{}{7, 9}, ""},
// Invalid conversions:
- {[]interface{}{"Brad", int64(0xFFFFFFFF)}, "sql: converting Exec argument #1's type: sql/driver: value 4294967295 overflows int32"},
- {[]interface{}{"Brad", "strconv fail"}, "sql: converting Exec argument #1's type: sql/driver: value \"strconv fail\" can't be converted to int32"},
+ {[]interface{}{"Brad", int64(0xFFFFFFFF)}, "sql: converting argument #1's type: sql/driver: value 4294967295 overflows int32"},
+ {[]interface{}{"Brad", "strconv fail"}, "sql: converting argument #1's type: sql/driver: value \"strconv fail\" can't be converted to int32"},
// Wrong number of args:
{[]interface{}{}, "sql: expected 2 arguments, got 0"},
@@ -402,6 +434,39 @@ func TestTxQueryInvalid(t *testing.T) {
}
}
+// Tests fix for issue 4433, that retries in Begin happen when
+// conn.Begin() returns ErrBadConn
+func TestTxErrBadConn(t *testing.T) {
+ db, err := Open("test", fakeDBName+";badConn")
+ if err != nil {
+ t.Fatalf("Open: %v", err)
+ }
+ if _, err := db.Exec("WIPE"); err != nil {
+ t.Fatalf("exec wipe: %v", err)
+ }
+ defer closeDB(t, db)
+ exec(t, db, "CREATE|t1|name=string,age=int32,dead=bool")
+ stmt, err := db.Prepare("INSERT|t1|name=?,age=?")
+ if err != nil {
+ t.Fatalf("Stmt, err = %v, %v", stmt, err)
+ }
+ defer stmt.Close()
+ tx, err := db.Begin()
+ if err != nil {
+ t.Fatalf("Begin = %v", err)
+ }
+ txs := tx.Stmt(stmt)
+ defer txs.Close()
+ _, err = txs.Exec("Bobby", 7)
+ if err != nil {
+ t.Fatalf("Exec = %v", err)
+ }
+ err = tx.Commit()
+ if err != nil {
+ t.Fatalf("Commit = %v", err)
+ }
+}
+
// Tests fix for issue 2542, that we release a lock when querying on
// a closed connection.
func TestIssue2542Deadlock(t *testing.T) {
@@ -415,6 +480,30 @@ func TestIssue2542Deadlock(t *testing.T) {
}
}
+// From golang.org/issue/3865
+func TestCloseStmtBeforeRows(t *testing.T) {
+ db := newTestDB(t, "people")
+ defer closeDB(t, db)
+
+ s, err := db.Prepare("SELECT|people|name|")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ r, err := s.Query()
+ if err != nil {
+ s.Close()
+ t.Fatal(err)
+ }
+
+ err = s.Close()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ r.Close()
+}
+
// Tests fix for issue 2788, that we bind nil to a []byte if the
// value in the column is sql null
func TestNullByteSlice(t *testing.T) {
@@ -608,7 +697,14 @@ func nullTestRun(t *testing.T, spec nullTestSpec) {
}
}
-func stack() string {
- buf := make([]byte, 1024)
- return string(buf[:runtime.Stack(buf, false)])
+// golang.org/issue/4859
+func TestQueryRowNilScanDest(t *testing.T) {
+ db := newTestDB(t, "people")
+ 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"
+ if err == nil || err.Error() != want {
+ t.Errorf("error = %q; want %q", err.Error(), want)
+ }
}