mirror of
https://github.com/golang/go
synced 2024-11-12 10:00:25 -07:00
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
This commit is contained in:
parent
8c30b3f038
commit
2968e239b0
@ -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 {
|
||||
stmt.Close()
|
||||
releaseConn(err)
|
||||
return nil, err
|
||||
}
|
||||
rows.closeStmt = stmt
|
||||
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
|
||||
}
|
||||
|
||||
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)
|
||||
ci, err := tx.grabConn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rows, err := stmt.Query(args...)
|
||||
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.
|
||||
|
Loading…
Reference in New Issue
Block a user