Commit 2968e239 authored by Julien Schmidt's avatar Julien Schmidt Committed by Brad Fitzpatrick

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
parent 8c30b3f0
......@@ -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.
//
......
......@@ -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...))
}
......
......@@ -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.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment