From 2968e239b00e3cfa4b9f146b00f06b01134ae5a1 Mon Sep 17 00:00:00 2001 From: Julien Schmidt Date: Wed, 13 Feb 2013 15:25:39 -0800 Subject: 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 --- src/pkg/database/sql/sql.go | 117 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 91 insertions(+), 26 deletions(-) (limited to 'src/pkg/database/sql/sql.go') 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. -- cgit v1.3