]> Cypherpunks repositories - gostls13.git/commitdiff
database/sql: Add an optional Queryer-Interface (like Execer)
authorJulien Schmidt <google@julienschmidt.com>
Wed, 13 Feb 2013 23:25:39 +0000 (15:25 -0800)
committerBrad Fitzpatrick <bradfitz@golang.org>
Wed, 13 Feb 2013 23:25:39 +0000 (15:25 -0800)
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/driver/driver.go
src/pkg/database/sql/fakedb_test.go
src/pkg/database/sql/sql.go

index 2f5280db810516be9d3ae53f1c3a4c56ff854f5b..7b235b9fbcbdfe076592887dc424883109e07c0a 100644 (file)
@@ -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.
 //
index c38ba7c849264036cc454873b38af736b5d1ad92..55597f7de3ba38628ba4dc50c4963529b4f1ec66 100644 (file)
@@ -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...))
 }
index 29aef78b2407e373ac2af62fe9cf80232c629f03..376390aa7197d2010a27c0df4fa1924ae5fed942 100644 (file)
@@ -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.