| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553 |
- // Copyright 2014 The ql Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- // database/sql/driver
- package ql
- import (
- "bytes"
- "database/sql"
- "database/sql/driver"
- "errors"
- "fmt"
- "io"
- "math/big"
- "net/url"
- "os"
- "path/filepath"
- "strconv"
- "strings"
- "sync"
- "time"
- )
- var (
- _ driver.Conn = (*driverConn)(nil)
- _ driver.Driver = (*sqlDriver)(nil)
- _ driver.Execer = (*driverConn)(nil)
- _ driver.Queryer = (*driverConn)(nil)
- _ driver.Result = (*driverResult)(nil)
- _ driver.Rows = (*driverRows)(nil)
- _ driver.Stmt = (*driverStmt)(nil)
- _ driver.Tx = (*driverConn)(nil)
- txBegin = MustCompile("BEGIN TRANSACTION;")
- txCommit = MustCompile("COMMIT;")
- txRollback = MustCompile("ROLLBACK;")
- errNoResult = errors.New("query statement does not produce a result set (no top level SELECT)")
- )
- type errList []error
- func (e *errList) append(err error) {
- if err != nil {
- *e = append(*e, err)
- }
- }
- func (e errList) error() error {
- if len(e) == 0 {
- return nil
- }
- return e
- }
- func (e errList) Error() string {
- a := make([]string, len(e))
- for i, v := range e {
- a[i] = v.Error()
- }
- return strings.Join(a, "\n")
- }
- func params(args []driver.Value) []interface{} {
- r := make([]interface{}, len(args))
- for i, v := range args {
- r[i] = interface{}(v)
- }
- return r
- }
- var (
- fileDriver = &sqlDriver{dbs: map[string]*driverDB{}}
- fileDriverOnce sync.Once
- memDriver = &sqlDriver{isMem: true, dbs: map[string]*driverDB{}}
- memDriverOnce sync.Once
- )
- // RegisterDriver registers a QL database/sql/driver[0] named "ql". The name
- // parameter of
- //
- // sql.Open("ql", name)
- //
- // is interpreted as a path name to a named DB file which will be created if
- // not present. The underlying QL database data are persisted on db.Close().
- // RegisterDriver can be safely called multiple times, it'll register the
- // driver only once.
- //
- // The name argument can be optionally prefixed by "file://". In that case the
- // prefix is stripped before interpreting it as a file name.
- //
- // The name argument can be optionally prefixed by "memory://". In that case
- // the prefix is stripped before interpreting it as a name of a memory-only,
- // volatile DB.
- //
- // [0]: http://golang.org/pkg/database/sql/driver/
- func RegisterDriver() {
- fileDriverOnce.Do(func() { sql.Register("ql", fileDriver) })
- }
- // RegisterMemDriver registers a QL memory database/sql/driver[0] named
- // "ql-mem". The name parameter of
- //
- // sql.Open("ql-mem", name)
- //
- // is interpreted as an unique memory DB name which will be created if not
- // present. The underlying QL memory database data are not persisted on
- // db.Close(). RegisterMemDriver can be safely called multiple times, it'll
- // register the driver only once.
- //
- // [0]: http://golang.org/pkg/database/sql/driver/
- func RegisterMemDriver() {
- memDriverOnce.Do(func() { sql.Register("ql-mem", memDriver) })
- }
- type driverDB struct {
- db *DB
- name string
- refcount int
- }
- func newDriverDB(db *DB, name string) *driverDB {
- return &driverDB{db: db, name: name, refcount: 1}
- }
- // sqlDriver implements the interface required by database/sql/driver.
- type sqlDriver struct {
- dbs map[string]*driverDB
- isMem bool
- mu sync.Mutex
- }
- func (d *sqlDriver) lock() func() {
- d.mu.Lock()
- return d.mu.Unlock
- }
- // Open returns a new connection to the database. The name is a string in a
- // driver-specific format.
- //
- // Open may return a cached connection (one previously closed), but doing so is
- // unnecessary; the sql package maintains a pool of idle connections for
- // efficient re-use.
- //
- // The returned connection is only used by one goroutine at a time.
- //
- // The name supported URL parameters:
- //
- // headroom Size of the WAL headroom. See https://github.com/cznic/ql/issues/140.
- func (d *sqlDriver) Open(name string) (driver.Conn, error) {
- switch {
- case d == fileDriver:
- if !strings.Contains(name, "://") && !strings.HasPrefix(name, "file") {
- name = "file://" + name
- }
- case d == memDriver:
- if !strings.Contains(name, "://") && !strings.HasPrefix(name, "memory") {
- name = "memory://" + name
- }
- default:
- return nil, fmt.Errorf("open: unexpected/unsupported instance of driver.Driver: %p", d)
- }
- name = filepath.ToSlash(name) // Ensure / separated URLs on Windows
- uri, err := url.Parse(name)
- if err != nil {
- return nil, err
- }
- switch uri.Scheme {
- case "file":
- // ok
- case "memory":
- d = memDriver
- default:
- return nil, fmt.Errorf("open: unexpected/unsupported scheme: %s", uri.Scheme)
- }
- name = filepath.Clean(filepath.Join(uri.Host, uri.Path))
- if d == fileDriver && (name == "" || name == "." || name == string(os.PathSeparator)) {
- return nil, fmt.Errorf("invalid DB name %q", name)
- }
- var headroom int64
- if a := uri.Query()["headroom"]; len(a) != 0 {
- if headroom, err = strconv.ParseInt(a[0], 10, 64); err != nil {
- return nil, err
- }
- }
- defer d.lock()()
- db := d.dbs[name]
- if db == nil {
- var err error
- var db0 *DB
- switch d.isMem {
- case true:
- db0, err = OpenMem()
- default:
- db0, err = OpenFile(name, &Options{CanCreate: true, Headroom: headroom})
- }
- if err != nil {
- return nil, err
- }
- db = newDriverDB(db0, name)
- d.dbs[name] = db
- return newDriverConn(d, db), nil
- }
- db.refcount++
- return newDriverConn(d, db), nil
- }
- // driverConn is a connection to a database. It is not used concurrently by
- // multiple goroutines.
- //
- // Conn is assumed to be stateful.
- type driverConn struct {
- ctx *TCtx
- db *driverDB
- driver *sqlDriver
- stop map[*driverStmt]struct{}
- tnl int
- }
- func newDriverConn(d *sqlDriver, ddb *driverDB) driver.Conn {
- r := &driverConn{
- db: ddb,
- driver: d,
- stop: map[*driverStmt]struct{}{},
- }
- return r
- }
- // Prepare returns a prepared statement, bound to this connection.
- func (c *driverConn) Prepare(query string) (driver.Stmt, error) {
- list, err := Compile(query)
- if err != nil {
- return nil, err
- }
- s := &driverStmt{conn: c, stmt: list}
- c.stop[s] = struct{}{}
- return s, nil
- }
- // Close invalidates and potentially stops any current prepared statements and
- // transactions, marking this connection as no longer in use.
- //
- // Because the sql package maintains a free pool of connections and only calls
- // Close when there's a surplus of idle connections, it shouldn't be necessary
- // for drivers to do their own connection caching.
- func (c *driverConn) Close() error {
- var err errList
- for s := range c.stop {
- err.append(s.Close())
- }
- defer c.driver.lock()()
- dbs, name := c.driver.dbs, c.db.name
- v := dbs[name]
- v.refcount--
- if v.refcount == 0 {
- err.append(c.db.db.Close())
- delete(dbs, name)
- }
- return err.error()
- }
- // Begin starts and returns a new transaction.
- func (c *driverConn) Begin() (driver.Tx, error) {
- if c.ctx == nil {
- c.ctx = NewRWCtx()
- }
- if _, _, err := c.db.db.Execute(c.ctx, txBegin); err != nil {
- return nil, err
- }
- c.tnl++
- return c, nil
- }
- func (c *driverConn) Commit() error {
- if c.tnl == 0 || c.ctx == nil {
- return errCommitNotInTransaction
- }
- if _, _, err := c.db.db.Execute(c.ctx, txCommit); err != nil {
- return err
- }
- c.tnl--
- if c.tnl == 0 {
- c.ctx = nil
- }
- return nil
- }
- func (c *driverConn) Rollback() error {
- if c.tnl == 0 || c.ctx == nil {
- return errRollbackNotInTransaction
- }
- if _, _, err := c.db.db.Execute(c.ctx, txRollback); err != nil {
- return err
- }
- c.tnl--
- if c.tnl == 0 {
- c.ctx = nil
- }
- return nil
- }
- // Execer is an optional interface that may be implemented by a Conn.
- //
- // If a Conn does not implement Execer, the sql package's DB.Exec will first
- // prepare a query, execute the statement, and then close the statement.
- //
- // Exec may return driver.ErrSkip.
- func (c *driverConn) Exec(query string, args []driver.Value) (driver.Result, error) {
- list, err := Compile(query)
- if err != nil {
- return nil, err
- }
- return driverExec(c.db, c.ctx, list, args)
- }
- func driverExec(db *driverDB, ctx *TCtx, list List, args []driver.Value) (driver.Result, error) {
- if _, _, err := db.db.Execute(ctx, list, params(args)...); err != nil {
- return nil, err
- }
- if len(list.l) == 1 {
- switch list.l[0].(type) {
- case *createTableStmt, *dropTableStmt, *alterTableAddStmt,
- *alterTableDropColumnStmt, *truncateTableStmt:
- return driver.ResultNoRows, nil
- }
- }
- r := &driverResult{}
- if ctx != nil {
- r.lastInsertID, r.rowsAffected = ctx.LastInsertID, ctx.RowsAffected
- }
- return r, nil
- }
- // Queryer is an optional interface that may be implemented by a Conn.
- //
- // If a Conn does not implement Queryer, the sql package's DB.Query will first
- // prepare a query, execute the statement, and then close the statement.
- //
- // Query may return driver.ErrSkip.
- func (c *driverConn) Query(query string, args []driver.Value) (driver.Rows, error) {
- list, err := Compile(query)
- if err != nil {
- return nil, err
- }
- return driverQuery(c.db, c.ctx, list, args)
- }
- func driverQuery(db *driverDB, ctx *TCtx, list List, args []driver.Value) (driver.Rows, error) {
- rss, _, err := db.db.Execute(ctx, list, params(args)...)
- if err != nil {
- return nil, err
- }
- switch n := len(rss); n {
- case 0:
- return nil, errNoResult
- case 1:
- return newdriverRows(rss[len(rss)-1]), nil
- default:
- return nil, fmt.Errorf("query produced %d result sets, expected only one", n)
- }
- }
- // driverResult is the result of a query execution.
- type driverResult struct {
- lastInsertID int64
- rowsAffected int64
- }
- // LastInsertId returns the database's auto-generated ID after, for example, an
- // INSERT into a table with primary key.
- func (r *driverResult) LastInsertId() (int64, error) { // -golint
- return r.lastInsertID, nil
- }
- // RowsAffected returns the number of rows affected by the query.
- func (r *driverResult) RowsAffected() (int64, error) {
- return r.rowsAffected, nil
- }
- // driverRows is an iterator over an executed query's results.
- type driverRows struct {
- rs Recordset
- done chan int
- rows chan interface{}
- }
- func newdriverRows(rs Recordset) *driverRows {
- r := &driverRows{
- rs: rs,
- done: make(chan int),
- rows: make(chan interface{}, 500),
- }
- go func() {
- err := io.EOF
- if e := r.rs.Do(false, func(data []interface{}) (bool, error) {
- select {
- case r.rows <- data:
- return true, nil
- case <-r.done:
- return false, nil
- }
- }); e != nil {
- err = e
- }
- select {
- case r.rows <- err:
- case <-r.done:
- }
- }()
- return r
- }
- // Columns returns the names of the columns. The number of columns of the
- // result is inferred from the length of the slice. If a particular column
- // name isn't known, an empty string should be returned for that entry.
- func (r *driverRows) Columns() []string {
- f, _ := r.rs.Fields()
- return f
- }
- // Close closes the rows iterator.
- func (r *driverRows) Close() error {
- close(r.done)
- return nil
- }
- // Next is called to populate the next row of data into the provided slice. The
- // provided slice will be the same size as the Columns() are wide.
- //
- // The dest slice may be populated only with a driver Value type, but excluding
- // string. All string values must be converted to []byte.
- //
- // Next should return io.EOF when there are no more rows.
- func (r *driverRows) Next(dest []driver.Value) error {
- select {
- case rx := <-r.rows:
- switch x := rx.(type) {
- case error:
- return x
- case []interface{}:
- if g, e := len(x), len(dest); g != e {
- return fmt.Errorf("field count mismatch: got %d, need %d", g, e)
- }
- for i, xi := range x {
- switch v := xi.(type) {
- case nil, int64, float64, bool, []byte, time.Time:
- dest[i] = v
- case complex64, complex128, *big.Int, *big.Rat:
- var buf bytes.Buffer
- fmt.Fprintf(&buf, "%v", v)
- dest[i] = buf.Bytes()
- case int8:
- dest[i] = int64(v)
- case int16:
- dest[i] = int64(v)
- case int32:
- dest[i] = int64(v)
- case int:
- dest[i] = int64(v)
- case uint8:
- dest[i] = int64(v)
- case uint16:
- dest[i] = int64(v)
- case uint32:
- dest[i] = int64(v)
- case uint64:
- dest[i] = int64(v)
- case uint:
- dest[i] = int64(v)
- case time.Duration:
- dest[i] = int64(v)
- case string:
- dest[i] = []byte(v)
- default:
- return fmt.Errorf("internal error 004")
- }
- }
- return nil
- default:
- return fmt.Errorf("internal error 005")
- }
- case <-r.done:
- return io.EOF
- }
- }
- // driverStmt is a prepared statement. It is bound to a driverConn and not used
- // by multiple goroutines concurrently.
- type driverStmt struct {
- conn *driverConn
- stmt List
- }
- // Close closes the statement.
- //
- // As of Go 1.1, a Stmt will not be closed if it's in use by any queries.
- func (s *driverStmt) Close() error {
- delete(s.conn.stop, s)
- return nil
- }
- // NumInput returns the number of placeholder parameters.
- //
- // If NumInput returns >= 0, the sql package will sanity check argument counts
- // from callers and return errors to the caller before the statement's Exec or
- // Query methods are called.
- //
- // NumInput may also return -1, if the driver doesn't know its number of
- // placeholders. In that case, the sql package will not sanity check Exec or
- // Query argument counts.
- func (s *driverStmt) NumInput() int {
- if x := s.stmt; len(x.l) == 1 {
- return x.params
- }
- return -1
- }
- // Exec executes a query that doesn't return rows, such as an INSERT or UPDATE.
- func (s *driverStmt) Exec(args []driver.Value) (driver.Result, error) {
- c := s.conn
- return driverExec(c.db, c.ctx, s.stmt, args)
- }
- // Exec executes a query that may return rows, such as a SELECT.
- func (s *driverStmt) Query(args []driver.Value) (driver.Rows, error) {
- c := s.conn
- return driverQuery(c.db, c.ctx, s.stmt, args)
- }
|