summaryrefslogtreecommitdiffstats
path: root/libgo/go/exp/sql/sql.go
diff options
context:
space:
mode:
authorian <ian@138bc75d-0d04-0410-961f-82ee72b054a4>2011-10-26 23:57:58 +0000
committerian <ian@138bc75d-0d04-0410-961f-82ee72b054a4>2011-10-26 23:57:58 +0000
commitfa5d125b5cfa5c935e46d27a2cbcd71ae37687ac (patch)
tree19d182df05ead7ff8ba7ee00a7d57555e1383fdf /libgo/go/exp/sql/sql.go
parente3d46e67996cf20ca3a75fccbb5a0007bfa3f992 (diff)
downloadppe42-gcc-fa5d125b5cfa5c935e46d27a2cbcd71ae37687ac.tar.gz
ppe42-gcc-fa5d125b5cfa5c935e46d27a2cbcd71ae37687ac.zip
Update Go library to last weekly.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@180552 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'libgo/go/exp/sql/sql.go')
-rw-r--r--libgo/go/exp/sql/sql.go578
1 files changed, 578 insertions, 0 deletions
diff --git a/libgo/go/exp/sql/sql.go b/libgo/go/exp/sql/sql.go
new file mode 100644
index 00000000000..7f0e0b28425
--- /dev/null
+++ b/libgo/go/exp/sql/sql.go
@@ -0,0 +1,578 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package sql provides a generic interface around SQL (or SQL-like)
+// databases.
+package sql
+
+import (
+ "fmt"
+ "os"
+ "runtime"
+ "sync"
+
+ "exp/sql/driver"
+)
+
+var drivers = make(map[string]driver.Driver)
+
+// Register makes a database driver available by the provided name.
+// If Register is called twice with the same name or if driver is nil,
+// it panics.
+func Register(name string, driver driver.Driver) {
+ if driver == nil {
+ panic("db: Register driver is nil")
+ }
+ if _, dup := drivers[name]; dup {
+ panic("db: Register called twice for driver " + name)
+ }
+ drivers[name] = driver
+}
+
+// NullableString represents a string that may be null.
+// NullableString implements the ScannerInto interface so
+// it can be used as a scan destination:
+//
+// var s NullableString
+// err := db.QueryRow("SELECT name FROM foo WHERE id=?", id).Scan(&s)
+// ...
+// if s.Valid {
+// // use s.String
+// } else {
+// // NULL value
+// }
+//
+// TODO(bradfitz): add other types.
+type NullableString struct {
+ String string
+ Valid bool // Valid is true if String is not NULL
+}
+
+// ScanInto implements the ScannerInto interface.
+func (ms *NullableString) ScanInto(value interface{}) os.Error {
+ if value == nil {
+ ms.String, ms.Valid = "", false
+ return nil
+ }
+ ms.Valid = true
+ return convertAssign(&ms.String, value)
+}
+
+// ScannerInto is an interface used by Scan.
+type ScannerInto interface {
+ // ScanInto assigns a value from a database driver.
+ //
+ // The value will be of one of the following restricted
+ // set of types:
+ //
+ // int64
+ // float64
+ // bool
+ // []byte
+ // nil - for NULL values
+ //
+ // An error should be returned if the value can not be stored
+ // without loss of information.
+ ScanInto(value interface{}) os.Error
+}
+
+// ErrNoRows is returned by Scan when QueryRow doesn't return a
+// row. In such a case, QueryRow returns a placeholder *Row value that
+// defers this error until a Scan.
+var ErrNoRows = os.NewError("db: no rows in result set")
+
+// DB is a database handle. It's safe for concurrent use by multiple
+// goroutines.
+type DB struct {
+ driver driver.Driver
+ dsn string
+
+ mu sync.Mutex
+ freeConn []driver.Conn
+}
+
+// Open opens a database specified by its database driver name and a
+// driver-specific data source name, usually consisting of at least a
+// database name and connection information.
+//
+// Most users will open a database via a driver-specific connection
+// helper function that returns a *DB.
+func Open(driverName, dataSourceName string) (*DB, os.Error) {
+ driver, ok := drivers[driverName]
+ if !ok {
+ return nil, fmt.Errorf("db: unknown driver %q (forgotten import?)", driverName)
+ }
+ return &DB{driver: driver, dsn: dataSourceName}, nil
+}
+
+func (db *DB) maxIdleConns() int {
+ const defaultMaxIdleConns = 2
+ // TODO(bradfitz): ask driver, if supported, for its default preference
+ // TODO(bradfitz): let users override?
+ return defaultMaxIdleConns
+}
+
+// conn returns a newly-opened or cached driver.Conn
+func (db *DB) conn() (driver.Conn, os.Error) {
+ db.mu.Lock()
+ if n := len(db.freeConn); n > 0 {
+ conn := db.freeConn[n-1]
+ db.freeConn = db.freeConn[:n-1]
+ db.mu.Unlock()
+ return conn, nil
+ }
+ db.mu.Unlock()
+ return db.driver.Open(db.dsn)
+}
+
+func (db *DB) connIfFree(wanted driver.Conn) (conn driver.Conn, ok bool) {
+ db.mu.Lock()
+ defer db.mu.Unlock()
+ for n, conn := range db.freeConn {
+ if conn == wanted {
+ db.freeConn[n] = db.freeConn[len(db.freeConn)-1]
+ db.freeConn = db.freeConn[:len(db.freeConn)-1]
+ return wanted, true
+ }
+ }
+ return nil, false
+}
+
+func (db *DB) putConn(c driver.Conn) {
+ if n := len(db.freeConn); n < db.maxIdleConns() {
+ db.freeConn = append(db.freeConn, c)
+ return
+ }
+ db.closeConn(c)
+}
+
+func (db *DB) closeConn(c driver.Conn) {
+ // TODO: check to see if we need this Conn for any prepared statements
+ // that are active.
+ c.Close()
+}
+
+// Prepare creates a prepared statement for later execution.
+func (db *DB) Prepare(query string) (*Stmt, os.Error) {
+ // TODO: check if db.driver supports an optional
+ // driver.Preparer interface and call that instead, if so,
+ // otherwise we make a prepared statement that's bound
+ // to a connection, and to execute this prepared statement
+ // we either need to use this connection (if it's free), else
+ // get a new connection + re-prepare + execute on that one.
+ ci, err := db.conn()
+ if err != nil {
+ return nil, err
+ }
+ defer db.putConn(ci)
+ si, err := ci.Prepare(query)
+ if err != nil {
+ return nil, err
+ }
+ stmt := &Stmt{
+ db: db,
+ query: query,
+ css: []connStmt{{ci, si}},
+ }
+ return stmt, nil
+}
+
+// Exec executes a query without returning any rows.
+func (db *DB) Exec(query string, args ...interface{}) (Result, os.Error) {
+ // Optional fast path, if the driver implements driver.Execer.
+ if execer, ok := db.driver.(driver.Execer); ok {
+ resi, err := execer.Exec(query, args)
+ if err != nil {
+ return nil, err
+ }
+ return result{resi}, nil
+ }
+
+ // If the driver does not implement driver.Execer, we need
+ // a connection.
+ conn, err := db.conn()
+ if err != nil {
+ return nil, err
+ }
+ defer db.putConn(conn)
+
+ if execer, ok := conn.(driver.Execer); ok {
+ resi, err := execer.Exec(query, args)
+ if err != nil {
+ return nil, err
+ }
+ return result{resi}, nil
+ }
+
+ sti, err := conn.Prepare(query)
+ if err != nil {
+ return nil, err
+ }
+ defer sti.Close()
+ resi, err := sti.Exec(args)
+ if err != nil {
+ return nil, err
+ }
+ return result{resi}, nil
+}
+
+// Query executes a query that returns rows, typically a SELECT.
+func (db *DB) Query(query string, args ...interface{}) (*Rows, os.Error) {
+ stmt, err := db.Prepare(query)
+ if err != nil {
+ return nil, err
+ }
+ defer stmt.Close()
+ return stmt.Query(args...)
+}
+
+// QueryRow executes a query that is expected to return at most one row.
+// QueryRow always return a non-nil value. Errors are deferred until
+// Row's Scan method is called.
+func (db *DB) QueryRow(query string, args ...interface{}) *Row {
+ rows, err := db.Query(query, args...)
+ if err != nil {
+ return &Row{err: err}
+ }
+ return &Row{rows: rows}
+}
+
+// Begin starts a transaction. The isolation level is dependent on
+// the driver.
+func (db *DB) Begin() (*Tx, os.Error) {
+ // TODO(bradfitz): add another method for beginning a transaction
+ // at a specific isolation level.
+ panic(todo())
+}
+
+// DriverDatabase returns the database's underlying driver.
+func (db *DB) Driver() driver.Driver {
+ return db.driver
+}
+
+// Tx is an in-progress database transaction.
+type Tx struct {
+
+}
+
+// Commit commits the transaction.
+func (tx *Tx) Commit() os.Error {
+ panic(todo())
+}
+
+// Rollback aborts the transaction.
+func (tx *Tx) Rollback() os.Error {
+ panic(todo())
+}
+
+// Prepare creates a prepared statement.
+func (tx *Tx) Prepare(query string) (*Stmt, os.Error) {
+ panic(todo())
+}
+
+// Exec executes a query that doesn't return rows.
+// For example: an INSERT and UPDATE.
+func (tx *Tx) Exec(query string, args ...interface{}) {
+ panic(todo())
+}
+
+// Query executes a query that returns rows, typically a SELECT.
+func (tx *Tx) Query(query string, args ...interface{}) (*Rows, os.Error) {
+ panic(todo())
+}
+
+// QueryRow executes a query that is expected to return at most one row.
+// QueryRow always return a non-nil value. Errors are deferred until
+// Row's Scan method is called.
+func (tx *Tx) QueryRow(query string, args ...interface{}) *Row {
+ panic(todo())
+}
+
+// connStmt is a prepared statement on a particular connection.
+type connStmt struct {
+ ci driver.Conn
+ si driver.Stmt
+}
+
+// Stmt is a prepared statement. Stmt is safe for concurrent use by multiple goroutines.
+type Stmt struct {
+ // Immutable:
+ db *DB // where we came from
+ query string // that created the Sttm
+
+ mu sync.Mutex
+ closed bool
+ css []connStmt // can use any that have idle connections
+}
+
+func todo() string {
+ _, file, line, _ := runtime.Caller(1)
+ return fmt.Sprintf("%s:%d: TODO: implement", file, line)
+}
+
+// Exec executes a prepared statement with the given arguments and
+// returns a Result summarizing the effect of the statement.
+func (s *Stmt) Exec(args ...interface{}) (Result, os.Error) {
+ ci, si, err := s.connStmt()
+ if err != nil {
+ return nil, err
+ }
+ defer s.db.putConn(ci)
+
+ if want := si.NumInput(); len(args) != want {
+ return nil, fmt.Errorf("db: expected %d arguments, got %d", want, len(args))
+ }
+
+ // Convert args to subset types.
+ if cc, ok := si.(driver.ColumnConverter); ok {
+ for n, arg := range args {
+ args[n], err = cc.ColumnConverter(n).ConvertValue(arg)
+ if err != nil {
+ return nil, fmt.Errorf("db: converting Exec argument #%d's type: %v", n, err)
+ }
+ if !driver.IsParameterSubsetType(args[n]) {
+ return nil, fmt.Errorf("db: driver ColumnConverter error converted %T to unsupported type %T",
+ arg, args[n])
+ }
+ }
+ } else {
+ for n, arg := range args {
+ args[n], err = driver.DefaultParameterConverter.ConvertValue(arg)
+ if err != nil {
+ return nil, fmt.Errorf("db: converting Exec argument #%d's type: %v", n, err)
+ }
+ }
+ }
+
+ resi, err := si.Exec(args)
+ if err != nil {
+ return nil, err
+ }
+ return result{resi}, nil
+}
+
+func (s *Stmt) connStmt(args ...interface{}) (driver.Conn, driver.Stmt, os.Error) {
+ s.mu.Lock()
+ if s.closed {
+ return nil, nil, os.NewError("db: statement is closed")
+ }
+ var cs connStmt
+ match := false
+ for _, v := range s.css {
+ // TODO(bradfitz): lazily clean up entries in this
+ // list with dead conns while enumerating
+ if _, match = s.db.connIfFree(cs.ci); match {
+ cs = v
+ break
+ }
+ }
+ s.mu.Unlock()
+
+ // Make a new conn if all are busy.
+ // TODO(bradfitz): or wait for one? make configurable later?
+ if !match {
+ ci, err := s.db.conn()
+ if err != nil {
+ return nil, nil, err
+ }
+ si, err := ci.Prepare(s.query)
+ if err != nil {
+ return nil, nil, err
+ }
+ s.mu.Lock()
+ cs = connStmt{ci, si}
+ s.css = append(s.css, cs)
+ s.mu.Unlock()
+ }
+
+ return cs.ci, cs.si, nil
+}
+
+// Query executes a prepared query statement with the given arguments
+// and returns the query results as a *Rows.
+func (s *Stmt) Query(args ...interface{}) (*Rows, os.Error) {
+ ci, si, err := s.connStmt(args...)
+ if err != nil {
+ return nil, err
+ }
+ if len(args) != si.NumInput() {
+ return nil, fmt.Errorf("db: statement expects %d inputs; got %d", si.NumInput(), len(args))
+ }
+ rowsi, err := si.Query(args)
+ if err != nil {
+ s.db.putConn(ci)
+ return nil, err
+ }
+ // Note: ownership of ci passes to the *Rows
+ rows := &Rows{
+ db: s.db,
+ ci: ci,
+ rowsi: rowsi,
+ }
+ return rows, nil
+}
+
+// QueryRow executes a prepared query statement with the given arguments.
+// If an error occurs during the execution of the statement, that error will
+// be returned by a call to Scan on the returned *Row, which is always non-nil.
+// If the query selects no rows, the *Row's Scan will return ErrNoRows.
+// Otherwise, the *Row's Scan scans the first selected row and discards
+// the rest.
+//
+// Example usage:
+//
+// var name string
+// err := nameByUseridStmt.QueryRow(id).Scan(&s)
+func (s *Stmt) QueryRow(args ...interface{}) *Row {
+ rows, err := s.Query(args...)
+ if err != nil {
+ return &Row{err: err}
+ }
+ return &Row{rows: rows}
+}
+
+// Close closes the statement.
+func (s *Stmt) Close() os.Error {
+ s.mu.Lock()
+ defer s.mu.Unlock() // TODO(bradfitz): move this unlock after 'closed = true'?
+ if s.closed {
+ return nil
+ }
+ s.closed = true
+ for _, v := range s.css {
+ if ci, match := s.db.connIfFree(v.ci); match {
+ v.si.Close()
+ s.db.putConn(ci)
+ } else {
+ // TODO(bradfitz): care that we can't close
+ // this statement because the statement's
+ // connection is in use?
+ }
+ }
+ return nil
+}
+
+// Rows is the result of a query. Its cursor starts before the first row
+// of the result set. Use Next to advance through the rows:
+//
+// rows, err := db.Query("SELECT ...")
+// ...
+// for rows.Next() {
+// var id int
+// var name string
+// err = rows.Scan(&id, &name)
+// ...
+// }
+// err = rows.Error() // get any Error encountered during iteration
+// ...
+type Rows struct {
+ db *DB
+ ci driver.Conn // owned; must be returned when Rows is closed
+ rowsi driver.Rows
+
+ closed bool
+ lastcols []interface{}
+ lasterr os.Error
+}
+
+// Next prepares the next result row for reading with the Scan method.
+// It returns true on success, false if there is no next result row.
+// Every call to Scan, even the first one, must be preceded by a call
+// to Next.
+func (rs *Rows) Next() bool {
+ if rs.closed {
+ return false
+ }
+ if rs.lasterr != nil {
+ return false
+ }
+ if rs.lastcols == nil {
+ rs.lastcols = make([]interface{}, len(rs.rowsi.Columns()))
+ }
+ rs.lasterr = rs.rowsi.Next(rs.lastcols)
+ return rs.lasterr == nil
+}
+
+// Error returns the error, if any, that was encountered during iteration.
+func (rs *Rows) Error() os.Error {
+ if rs.lasterr == os.EOF {
+ return nil
+ }
+ return rs.lasterr
+}
+
+// Scan copies the columns in the current row into the values pointed
+// at by dest. If dest contains pointers to []byte, the slices should
+// not be modified and should only be considered valid until the next
+// call to Next or Scan.
+func (rs *Rows) Scan(dest ...interface{}) os.Error {
+ if rs.closed {
+ return os.NewError("db: Rows closed")
+ }
+ if rs.lasterr != nil {
+ return rs.lasterr
+ }
+ if rs.lastcols == nil {
+ return os.NewError("db: Scan called without calling Next")
+ }
+ if len(dest) != len(rs.lastcols) {
+ return fmt.Errorf("db: expected %d destination arguments in Scan, not %d", len(rs.lastcols), len(dest))
+ }
+ for i, sv := range rs.lastcols {
+ err := convertAssign(dest[i], sv)
+ if err != nil {
+ return fmt.Errorf("db: Scan error on column index %d: %v", i, err)
+ }
+ }
+ return nil
+}
+
+// Close closes the Rows, preventing further enumeration. If the
+// end is encountered, the Rows are closed automatically. Close
+// is idempotent.
+func (rs *Rows) Close() os.Error {
+ if rs.closed {
+ return nil
+ }
+ rs.closed = true
+ err := rs.rowsi.Close()
+ rs.db.putConn(rs.ci)
+ return err
+}
+
+// Row is the result of calling QueryRow to select a single row.
+type Row struct {
+ // One of these two will be non-nil:
+ err os.Error // deferred error for easy chaining
+ rows *Rows
+}
+
+// Scan copies the columns from the matched row into the values
+// pointed at by dest. If more than one row matches the query,
+// Scan uses the first row and discards the rest. If no row matches
+// the query, Scan returns ErrNoRows.
+//
+// If dest contains pointers to []byte, the slices should not be
+// modified and should only be considered valid until the next call to
+// Next or Scan.
+func (r *Row) Scan(dest ...interface{}) os.Error {
+ if r.err != nil {
+ return r.err
+ }
+ defer r.rows.Close()
+ if !r.rows.Next() {
+ return ErrNoRows
+ }
+ return r.rows.Scan(dest...)
+}
+
+// A Result summarizes an executed SQL command.
+type Result interface {
+ LastInsertId() (int64, os.Error)
+ RowsAffected() (int64, os.Error)
+}
+
+type result struct {
+ driver.Result
+}
OpenPOWER on IntegriCloud