From: Julien Schmidt Date: Wed, 13 Feb 2013 23:25:39 +0000 (-0800) Subject: database/sql: Add an optional Queryer-Interface (like Execer) X-Git-Tag: go1.1rc2~1048 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=2968e239b00e3cfa4b9f146b00f06b01134ae5a1;p=gostls13.git database/sql: Add an optional Queryer-Interface (like Execer) Completly the same like the Execer-Interface, just for Queries. This allows Drivers to execute Queries without preparing them first R=golang-dev, bradfitz CC=golang-dev https://golang.org/cl/7085056 --- diff --git a/src/pkg/database/sql/driver/driver.go b/src/pkg/database/sql/driver/driver.go index 2f5280db81..7b235b9fbc 100644 --- a/src/pkg/database/sql/driver/driver.go +++ b/src/pkg/database/sql/driver/driver.go @@ -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 db 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. // diff --git a/src/pkg/database/sql/fakedb_test.go b/src/pkg/database/sql/fakedb_test.go index c38ba7c849..55597f7de3 100644 --- a/src/pkg/database/sql/fakedb_test.go +++ b/src/pkg/database/sql/fakedb_test.go @@ -266,6 +266,18 @@ func (c *fakeConn) Exec(query string, args []driver.Value) (driver.Result, error 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) + if err != nil { + return nil, err + } + return nil, driver.ErrSkip +} + func errf(msg string, args ...interface{}) error { return errors.New("fakedb: " + fmt.Sprintf(msg, args...)) } diff --git a/src/pkg/database/sql/sql.go b/src/pkg/database/sql/sql.go index 29aef78b24..376390aa71 100644 --- a/src/pkg/database/sql/sql.go +++ b/src/pkg/database/sql/sql.go @@ -328,6 +328,7 @@ func (db *DB) prepare(query string) (stmt *Stmt, err error) { } // 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) { var res Result var err error @@ -375,16 +376,77 @@ func (db *DB) exec(query string, args []interface{}) (res Result, err error) { // 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) { - stmt, err := db.Prepare(query) + 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 } - rows, err := stmt.Query(args...) + + releaseConn := func(err error) { db.putConn(ci, err) } + + return db.queryConn(ci, releaseConn, query, args) +} + +// 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 { - stmt.Close() + releaseConn(err) return nil, err } - rows.closeStmt = stmt + + rowsi, err := rowsiFromStatement(sti, args...) + if err != nil { + releaseConn(err) + sti.Close() + 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, + closeStmt: sti, + } return rows, nil } @@ -605,20 +667,14 @@ func (tx *Tx) Exec(query string, args ...interface{}) (Result, error) { // 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. @@ -762,6 +818,24 @@ func (s *Stmt) Query(args ...interface{}) (*Rows, error) { 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, + releaseConn: releaseConn, + rowsi: rowsi, + } + 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. @@ -776,18 +850,9 @@ func (s *Stmt) Query(args ...interface{}) (*Rows, error) { 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. @@ -860,7 +925,7 @@ type Rows struct { 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.