syncthing/vendor/github.com/cznic/ql/driver.go
2017-04-05 14:34:41 +00:00

554 lines
13 KiB
Go

// 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)
}