aboutsummaryrefslogtreecommitdiff
path: root/sqlite_windows.go
diff options
context:
space:
mode:
Diffstat (limited to 'sqlite_windows.go')
-rw-r--r--sqlite_windows.go1406
1 files changed, 1406 insertions, 0 deletions
diff --git a/sqlite_windows.go b/sqlite_windows.go
new file mode 100644
index 0000000..4d289d3
--- /dev/null
+++ b/sqlite_windows.go
@@ -0,0 +1,1406 @@
+// Copyright 2017 The Sqlite 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 sqlite
+
+import (
+ "bytes"
+ "compress/gzip"
+ "database/sql"
+ "database/sql/driver"
+ "encoding/gob"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "runtime"
+ "sync"
+ "time"
+ "unsafe"
+
+ "github.com/cznic/internal/buffer"
+ "github.com/cznic/ir"
+ "github.com/cznic/mathutil"
+ "github.com/cznic/sqlite/internal/bin"
+ "github.com/cznic/virtual"
+ "github.com/cznic/xc"
+ "golang.org/x/net/context"
+)
+
+var (
+ _ driver.Conn = (*conn)(nil)
+ _ driver.Driver = (*Driver)(nil)
+ _ driver.Execer = (*conn)(nil)
+ _ driver.Queryer = (*conn)(nil)
+ _ driver.Result = (*result)(nil)
+ _ driver.Rows = (*rows)(nil)
+ _ driver.Stmt = (*stmt)(nil)
+ _ driver.Tx = (*tx)(nil)
+ _ io.Writer = debugWriter{}
+)
+
+const (
+ driverName = "sqlite"
+ ptrSize = mathutil.UintPtrBits / 8
+ vmHeapReserve = 1 << 20
+ vmHeapSize = 32 << 20
+ vmMainStackSize = 1 << 16
+ vmStackSize = 1 << 18
+)
+
+var (
+ binary virtual.Binary
+ dict = xc.Dict
+ null = virtual.Ptr(0)
+ vm *virtual.Machine
+
+ // FFI
+ bindBlob int
+ bindDouble int
+ bindInt int
+ bindInt64 int
+ bindParameterCount int
+ bindParameterName int
+ bindText int
+ changes int
+ closeV2 int
+ columnBlob int
+ columnBytes int
+ columnCount int
+ columnDouble int
+ columnInt64 int
+ columnName int
+ columnText int
+ columnType int
+ errmsg int
+ errstr int
+ exec int
+ extendedResultCodes int
+ finalize int
+ free int
+ interrupt int
+ lastInsertRowID int
+ maloc int
+ openV2 int
+ prepareV2 int
+ step int
+)
+
+func init() {
+ b0 := bytes.NewBufferString(bin.Data)
+ decomp, err := gzip.NewReader(b0)
+ if err != nil {
+ panic(err)
+ }
+
+ var b1 bytes.Buffer
+ chunk := make([]byte, 1<<15)
+ for {
+ n, err := decomp.Read(chunk)
+ b1.Write(chunk[:n])
+ if err != nil {
+ if err != io.EOF {
+ panic(err)
+ }
+
+ break
+ }
+ }
+ dec := gob.NewDecoder(&b1)
+ if err := dec.Decode(&binary); err != nil {
+ panic(err)
+ }
+
+ for _, v := range []struct {
+ *int
+ string
+ }{
+ {&bindBlob, "sqlite3_bind_blob"},
+ {&bindDouble, "sqlite3_bind_double"},
+ {&bindInt, "sqlite3_bind_int"},
+ {&bindInt64, "sqlite3_bind_int64"},
+ {&bindParameterCount, "sqlite3_bind_parameter_count"},
+ {&bindParameterName, "sqlite3_bind_parameter_name"},
+ {&bindText, "sqlite3_bind_text"},
+ {&changes, "sqlite3_changes"},
+ {&closeV2, "sqlite3_close_v2"},
+ {&columnBlob, "sqlite3_column_blob"},
+ {&columnBytes, "sqlite3_column_bytes"},
+ {&columnCount, "sqlite3_column_count"},
+ {&columnDouble, "sqlite3_column_double"},
+ {&columnInt64, "sqlite3_column_int64"},
+ {&columnName, "sqlite3_column_name"},
+ {&columnText, "sqlite3_column_text"},
+ {&columnType, "sqlite3_column_type"},
+ {&errmsg, "sqlite3_errmsg"},
+ {&errstr, "sqlite3_errstr"},
+ {&exec, "sqlite3_exec"},
+ {&extendedResultCodes, "sqlite3_extended_result_codes"},
+ {&finalize, "sqlite3_finalize"},
+ {&free, "sqlite3_free"},
+ {&interrupt, "sqlite3_interrupt"},
+ {&lastInsertRowID, "sqlite3_last_insert_rowid"},
+ {&maloc, "sqlite3_malloc"},
+ {&openV2, "sqlite3_open_v2"},
+ {&prepareV2, "sqlite3_prepare_v2"},
+ {&step, "sqlite3_step"},
+ } {
+ var ok bool
+ if *v.int, ok = binary.Sym[ir.NameID(dict.SID(v.string))]; !ok {
+ panic(fmt.Errorf("missing symbol: %v", v.string))
+ }
+ }
+
+ sql.Register(driverName, newDrv())
+}
+
+func tracer(rx interface{}, format string, args ...interface{}) {
+ var b buffer.Bytes
+ _, file, line, _ := runtime.Caller(1)
+ fmt.Fprintf(&b, "%v:%v: (%[3]T)(%[3]p).", file, line, rx)
+ fmt.Fprintf(&b, format, args...)
+ fmt.Fprintf(os.Stderr, "%s\n", b.Bytes())
+ b.Close()
+}
+
+func readI8(p uintptr) int8 { return *(*int8)(unsafe.Pointer(p)) }
+func readPtr(p uintptr) uintptr { return *(*uintptr)(unsafe.Pointer(p)) }
+
+type debugWriter struct{}
+
+func (debugWriter) Write(b []byte) (int, error) { return os.Stderr.Write(b) }
+
+type result struct {
+ *stmt
+ lastInsertId int64
+ rowsAffected int
+}
+
+func (r *result) String() string {
+ return fmt.Sprintf("&%T@%p{stmt: %p, LastInsertId: %v, RowsAffected: %v}", *r, r, r.stmt, r.lastInsertId, r.rowsAffected)
+}
+
+func newResult(s *stmt) (_ *result, err error) {
+ r := &result{stmt: s}
+ if r.rowsAffected, err = r.changes(); err != nil {
+ return nil, err
+ }
+
+ if r.lastInsertId, err = r.lastInsertRowID(); err != nil {
+ return nil, err
+ }
+
+ return r, nil
+}
+
+// sqlite3_int64 sqlite3_last_insert_rowid(sqlite3*);
+func (r *result) lastInsertRowID() (v int64, _ error) {
+ _, err := r.FFI1(
+ lastInsertRowID,
+ virtual.Int64Result{&v},
+ virtual.Ptr(r.pdb()),
+ )
+ return v, err
+}
+
+// int sqlite3_changes(sqlite3*);
+func (r *result) changes() (int, error) {
+ var v int32
+ _, err := r.FFI1(
+ changes,
+ virtual.Int32Result{&v},
+ virtual.Ptr(r.pdb()),
+ )
+ return int(v), err
+}
+
+// LastInsertId returns the database's auto-generated ID after, for example, an
+// INSERT into a table with primary key.
+func (r *result) LastInsertId() (int64, error) {
+ if r == nil {
+ return 0, nil
+ }
+
+ return r.lastInsertId, nil
+}
+
+// RowsAffected returns the number of rows affected by the query.
+func (r *result) RowsAffected() (int64, error) {
+ if r == nil {
+ return 0, nil
+ }
+
+ return int64(r.rowsAffected), nil
+}
+
+type rows struct {
+ *stmt
+ columns []string
+ rc0 int
+ pstmt uintptr
+ doStep bool
+}
+
+func (r *rows) String() string {
+ return fmt.Sprintf("&%T@%p{stmt: %p, columns: %v, rc0: %v, pstmt: %#x, doStep: %v}", *r, r, r.stmt, r.columns, r.rc0, r.pstmt, r.doStep)
+}
+
+func newRows(s *stmt, pstmt uintptr, rc0 int) (*rows, error) {
+ r := &rows{
+ stmt: s,
+ pstmt: pstmt,
+ rc0: rc0,
+ }
+
+ n, err := r.columnCount()
+ if err != nil {
+ return nil, err
+ }
+
+ r.columns = make([]string, n)
+ for i := range r.columns {
+ if r.columns[i], err = r.columnName(i); err != nil {
+ return nil, err
+ }
+ }
+
+ return r, nil
+}
+
+// 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 *rows) Columns() (c []string) {
+ if trace {
+ defer func() {
+ tracer(r, "Columns(): %v", c)
+ }()
+ }
+ return r.columns
+}
+
+// Close closes the rows iterator.
+func (r *rows) Close() (err error) {
+ if trace {
+ defer func() {
+ tracer(r, "Close(): %v", err)
+ }()
+ }
+ return r.finalize(r.pstmt)
+}
+
+// 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.
+//
+// Next should return io.EOF when there are no more rows.
+func (r *rows) Next(dest []driver.Value) (err error) {
+ if trace {
+ defer func() {
+ tracer(r, "Next(%v): %v", dest, err)
+ }()
+ }
+ rc := r.rc0
+ if r.doStep {
+ if rc, err = r.step(r.pstmt); err != nil {
+ return err
+ }
+ }
+
+ r.doStep = true
+
+ switch rc {
+ case bin.XSQLITE_ROW:
+ if g, e := len(dest), len(r.columns); g != e {
+ return fmt.Errorf("Next(): have %v destination values, expected %v", g, e)
+ }
+
+ for i := range dest {
+ ct, err := r.columnType(i)
+ if err != nil {
+ return err
+ }
+
+ switch ct {
+ case bin.XSQLITE_INTEGER:
+ v, err := r.columnInt64(i)
+ if err != nil {
+ return err
+ }
+
+ dest[i] = v
+ case bin.XSQLITE_FLOAT:
+ v, err := r.columnDouble(i)
+ if err != nil {
+ return err
+ }
+
+ dest[i] = v
+ case bin.XSQLITE_TEXT:
+ v, err := r.columnText(i)
+ if err != nil {
+ return err
+ }
+
+ dest[i] = v
+ case bin.XSQLITE_BLOB:
+ v, err := r.columnBlob(i)
+ if err != nil {
+ return err
+ }
+
+ dest[i] = v
+ case bin.XSQLITE_NULL:
+ dest[i] = nil
+ default:
+ panic("internal error")
+ }
+ }
+ return nil
+ case bin.XSQLITE_DONE:
+ return io.EOF
+ default:
+ return r.errstr(int32(rc))
+ }
+}
+
+// int sqlite3_column_bytes(sqlite3_stmt*, int iCol);
+func (r *rows) columnBytes(iCol int) (_ int, err error) {
+ var v int32
+ if _, err = r.FFI1(
+ columnBytes,
+ virtual.Int32Result{&v},
+ virtual.Ptr(r.pstmt), virtual.Int32(iCol),
+ ); err != nil {
+ return 0, err
+ }
+
+ return int(v), err
+}
+
+// const void *sqlite3_column_blob(sqlite3_stmt*, int iCol);
+func (r *rows) columnBlob(iCol int) (v []byte, err error) {
+ var p uintptr
+ if _, err = r.FFI1(
+ columnBlob,
+ virtual.PtrResult{&p},
+ virtual.Ptr(r.pstmt), virtual.Int32(iCol),
+ ); err != nil {
+ return nil, err
+ }
+
+ len, err := r.columnBytes(iCol)
+ if err != nil {
+ return nil, err
+ }
+
+ return virtual.GoBytesLen(p, len), nil
+}
+
+// const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol);
+func (r *rows) columnText(iCol int) (v string, err error) {
+ var p uintptr
+ if _, err = r.FFI1(
+ columnText,
+ virtual.PtrResult{&p},
+ virtual.Ptr(r.pstmt), virtual.Int32(iCol),
+ ); err != nil {
+ return "", err
+ }
+
+ len, err := r.columnBytes(iCol)
+ if err != nil {
+ return "", err
+ }
+
+ return virtual.GoStringLen(p, len), nil
+}
+
+// double sqlite3_column_double(sqlite3_stmt*, int iCol);
+func (r *rows) columnDouble(iCol int) (v float64, err error) {
+ _, err = r.FFI1(
+ columnDouble,
+ virtual.Float64Result{&v},
+ virtual.Ptr(r.pstmt), virtual.Int32(iCol),
+ )
+ return v, err
+}
+
+// sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol);
+func (r *rows) columnInt64(iCol int) (v int64, err error) {
+ _, err = r.FFI1(
+ columnInt64,
+ virtual.Int64Result{&v},
+ virtual.Ptr(r.pstmt), virtual.Int32(iCol),
+ )
+ return v, err
+}
+
+// int sqlite3_column_type(sqlite3_stmt*, int iCol);
+func (r *rows) columnType(iCol int) (_ int, err error) {
+ var v int32
+ _, err = r.FFI1(
+ columnType,
+ virtual.Int32Result{&v},
+ virtual.Ptr(r.pstmt), virtual.Int32(iCol),
+ )
+ return int(v), err
+}
+
+// int sqlite3_column_count(sqlite3_stmt *pStmt);
+func (r *rows) columnCount() (_ int, err error) {
+ var v int32
+ _, err = r.FFI1(
+ columnCount,
+ virtual.Int32Result{&v},
+ virtual.Ptr(r.pstmt),
+ )
+ return int(v), err
+}
+
+// const char *sqlite3_column_name(sqlite3_stmt*, int N);
+func (r *rows) columnName(n int) (string, error) {
+ var p uintptr
+ if _, err := r.FFI1(
+ columnName,
+ virtual.PtrResult{&p},
+ virtual.Ptr(r.pstmt), virtual.Int32(n),
+ ); err != nil {
+ return "", err
+ }
+
+ return virtual.GoString(p), nil
+}
+
+type stmt struct {
+ *conn
+ allocs []uintptr
+ psql uintptr
+ ppstmt uintptr
+ pzTail uintptr
+}
+
+func (s *stmt) String() string {
+ return fmt.Sprintf("&%T@%p{conn: %p, alloc %v, psql: %#x, ppstmt: %#x, pzTail: %#x}", *s, s, s.conn, s.allocs, s.psql, s.ppstmt, s.pzTail)
+}
+
+func newStmt(c *conn, sql string) (*stmt, error) {
+ s := &stmt{conn: c}
+ psql, err := s.cString(sql)
+ if err != nil {
+ return nil, err
+ }
+
+ s.psql = psql
+ ppstmt, err := s.malloc(ptrSize)
+ if err != nil {
+ s.free(psql)
+ return nil, err
+ }
+
+ s.ppstmt = ppstmt
+ pzTail, err := s.malloc(ptrSize)
+ if err != nil {
+ s.free(psql)
+ s.free(ppstmt)
+ return nil, err
+ }
+
+ s.pzTail = pzTail
+ return s, nil
+}
+
+// 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 *stmt) Close() (err error) {
+ if trace {
+ defer func() {
+ tracer(s, "Close(): %v", err)
+ }()
+ }
+ if s.psql != 0 {
+ err = s.free(s.psql)
+ s.psql = 0
+ }
+ if s.ppstmt != 0 {
+ if err2 := s.free(s.ppstmt); err2 != nil && err == nil {
+ err = err2
+ }
+ s.ppstmt = 0
+ }
+ if s.pzTail != 0 {
+ if err2 := s.free(s.pzTail); err2 != nil && err == nil {
+ err = err2
+ }
+ s.pzTail = 0
+ }
+ for _, v := range s.allocs {
+ if err2 := s.free(v); err2 != nil && err == nil {
+ err = err2
+ }
+ }
+ s.allocs = nil
+ return err
+}
+
+// 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 *stmt) NumInput() (n int) {
+ if trace {
+ defer func() {
+ tracer(s, "NumInput(): %v", n)
+ }()
+ }
+ return -1
+}
+
+// Exec executes a query that doesn't return rows, such as an INSERT or UPDATE.
+func (s *stmt) Exec(args []driver.Value) (driver.Result, error) {
+ return s.exec(context.Background(), toNamedValues(args))
+}
+
+func (s *stmt) exec(ctx context.Context, args []namedValue) (r driver.Result, err error) {
+ if trace {
+ defer func(args []namedValue) {
+ tracer(s, "Exec(%v): (%v, %v)", args, r, err)
+ }(args)
+ }
+
+ var pstmt uintptr
+
+ donech := make(chan struct{})
+ defer close(donech)
+ go func() {
+ select {
+ case <-ctx.Done():
+ if pstmt != 0 {
+ s.interrupt(s.pdb())
+ }
+ case <-donech:
+ }
+ }()
+
+ for psql := s.psql; readI8(psql) != 0; psql = readPtr(s.pzTail) {
+ if err := s.prepareV2(psql); err != nil {
+ return nil, err
+ }
+
+ pstmt = readPtr(s.ppstmt)
+ if pstmt == 0 {
+ continue
+ }
+
+ n, err := s.bindParameterCount(pstmt)
+ if err != nil {
+ return nil, err
+ }
+
+ if n != 0 {
+ if err = s.bind(pstmt, n, args); err != nil {
+ return nil, err
+ }
+ }
+
+ rc, err := s.step(pstmt)
+ if err != nil {
+ s.finalize(pstmt)
+ return nil, err
+ }
+
+ switch rc & 0xff {
+ case bin.XSQLITE_DONE, bin.XSQLITE_ROW:
+ if err := s.finalize(pstmt); err != nil {
+ return nil, err
+ }
+ default:
+ err = s.errstr(int32(rc))
+ s.finalize(pstmt)
+ return nil, err
+ }
+ }
+ return newResult(s)
+}
+
+func (s *stmt) Query(args []driver.Value) (driver.Rows, error) {
+ return s.query(context.Background(), toNamedValues(args))
+}
+
+func (s *stmt) query(ctx context.Context, args []namedValue) (r driver.Rows, err error) {
+ if trace {
+ defer func(args []namedValue) {
+ tracer(s, "Query(%v): (%v, %v)", args, r, err)
+ }(args)
+ }
+
+ var pstmt uintptr
+ var rowStmt uintptr
+ var rc0 int
+
+ donech := make(chan struct{})
+ defer close(donech)
+ go func() {
+ select {
+ case <-ctx.Done():
+ if pstmt != 0 {
+ s.interrupt(s.pdb())
+ }
+ case <-donech:
+ }
+ }()
+
+ for psql := s.psql; readI8(psql) != 0; psql = readPtr(s.pzTail) {
+ if err := s.prepareV2(psql); err != nil {
+ return nil, err
+ }
+
+ pstmt = readPtr(s.ppstmt)
+ if pstmt == 0 {
+ continue
+ }
+
+ n, err := s.bindParameterCount(pstmt)
+ if err != nil {
+ return nil, err
+ }
+
+ if n != 0 {
+ if err = s.bind(pstmt, n, args); err != nil {
+ return nil, err
+ }
+ }
+
+ rc, err := s.step(pstmt)
+ if err != nil {
+ s.finalize(pstmt)
+ return nil, err
+ }
+
+ switch rc {
+ case bin.XSQLITE_ROW:
+ if rowStmt != 0 {
+ if err := s.finalize(pstmt); err != nil {
+ return nil, err
+ }
+
+ return nil, fmt.Errorf("query contains multiple select statements")
+ }
+
+ rowStmt = pstmt
+ rc0 = rc
+ case bin.XSQLITE_DONE:
+ if rowStmt == 0 {
+ rc0 = rc
+ }
+ default:
+ err = s.errstr(int32(rc))
+ s.finalize(pstmt)
+ return nil, err
+ }
+ }
+ return newRows(s, rowStmt, rc0)
+}
+
+// int sqlite3_bind_double(sqlite3_stmt*, int, double);
+func (s *stmt) bindDouble(pstmt uintptr, idx1 int, value float64) (err error) {
+ var rc int32
+ if _, err = s.FFI1(
+ bindDouble,
+ virtual.Int32Result{&rc},
+ virtual.Ptr(pstmt), virtual.Int32(int32(idx1)), virtual.Float64(value),
+ ); err != nil {
+ return err
+ }
+
+ if rc != bin.XSQLITE_OK {
+ return s.errstr(rc)
+ }
+
+ return nil
+}
+
+// int sqlite3_bind_int(sqlite3_stmt*, int, int);
+func (s *stmt) bindInt(pstmt uintptr, idx1, value int) (err error) {
+ var rc int32
+ if _, err = s.FFI1(
+ bindInt,
+ virtual.Int32Result{&rc},
+ virtual.Ptr(pstmt), virtual.Int32(int32(idx1)), virtual.Int32(int32(value)),
+ ); err != nil {
+ return err
+ }
+
+ if rc != bin.XSQLITE_OK {
+ return s.errstr(rc)
+ }
+
+ return nil
+}
+
+// int sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64);
+func (s *stmt) bindInt64(pstmt uintptr, idx1 int, value int64) (err error) {
+ var rc int32
+ if _, err = s.FFI1(
+ bindInt64,
+ virtual.Int32Result{&rc},
+ virtual.Ptr(pstmt), virtual.Int32(int32(idx1)), virtual.Int64(value),
+ ); err != nil {
+ return err
+ }
+
+ if rc != bin.XSQLITE_OK {
+ return s.errstr(rc)
+ }
+
+ return nil
+}
+
+// int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*));
+func (s *stmt) bindBlob(pstmt uintptr, idx1 int, value []byte) (err error) {
+ p, err := s.malloc(len(value))
+ if err != nil {
+ return err
+ }
+
+ s.allocs = append(s.allocs, p)
+ virtual.CopyBytes(p, value, false)
+ var rc int32
+ if _, err = s.FFI1(
+ bindBlob,
+ virtual.Int32Result{&rc},
+ virtual.Ptr(pstmt), virtual.Int32(int32(idx1)), virtual.Ptr(p), virtual.Int32(int32(len(value))), null,
+ ); err != nil {
+ return err
+ }
+
+ if rc != bin.XSQLITE_OK {
+ return s.errstr(rc)
+ }
+
+ return nil
+}
+
+// int sqlite3_bind_text(sqlite3_stmt*,int,const char*,int,void(*)(void*));
+func (s *stmt) bindText(pstmt uintptr, idx1 int, value string) (err error) {
+ p, err := s.cString(value)
+ if err != nil {
+ return err
+ }
+
+ s.allocs = append(s.allocs, p)
+ var rc int32
+ if _, err = s.FFI1(
+ bindText,
+ virtual.Int32Result{&rc},
+ virtual.Ptr(pstmt), virtual.Int32(int32(idx1)), virtual.Ptr(p), virtual.Int32(int32(len(value))), null,
+ ); err != nil {
+ return err
+ }
+
+ if rc != bin.XSQLITE_OK {
+ return s.errstr(rc)
+ }
+
+ return nil
+}
+
+func (s *stmt) bind(pstmt uintptr, n int, args []namedValue) error {
+ for i := 1; i <= n; i++ {
+ name, err := s.bindParameterName(pstmt, i)
+ if err != nil {
+ return err
+ }
+
+ var v namedValue
+ for _, v = range args {
+ if name != "" {
+ // sqlite supports '$', '@' and ':' prefixes for string
+ // identifiers and '?' for numeric, so we cannot
+ // combine different prefixes with the same name
+ // because `database/sql` requires variable names
+ // to start with a letter
+ if name[1:] == v.Name[:] {
+ break
+ }
+ } else {
+ if v.Ordinal == i {
+ break
+ }
+ }
+ }
+
+ if v.Ordinal == 0 {
+ if name != "" {
+ return fmt.Errorf("missing named argument %q", name[1:])
+ } else {
+ return fmt.Errorf("missing argument with %d index", i)
+ }
+ }
+
+ switch x := v.Value.(type) {
+ case int64:
+ if err := s.bindInt64(pstmt, i, x); err != nil {
+ return err
+ }
+ case float64:
+ if err := s.bindDouble(pstmt, i, x); err != nil {
+ return err
+ }
+ case bool:
+ v := 0
+ if x {
+ v = 1
+ }
+ if err := s.bindInt(pstmt, i, v); err != nil {
+ return err
+ }
+ case []byte:
+ if err := s.bindBlob(pstmt, i, x); err != nil {
+ return err
+ }
+ case string:
+ if err := s.bindText(pstmt, i, x); err != nil {
+ return err
+ }
+ case time.Time:
+ if err := s.bindText(pstmt, i, x.String()); err != nil {
+ return err
+ }
+ default:
+ return fmt.Errorf("invalid driver.Value type %T", x)
+ }
+ }
+ return nil
+}
+
+// int sqlite3_bind_parameter_count(sqlite3_stmt*);
+func (s *stmt) bindParameterCount(pstmt uintptr) (_ int, err error) {
+ var r int32
+ _, err = s.FFI1(
+ bindParameterCount,
+ virtual.Int32Result{&r},
+ virtual.Ptr(pstmt),
+ )
+ return int(r), err
+}
+
+// const char *sqlite3_bind_parameter_name(sqlite3_stmt*, int);
+func (s *stmt) bindParameterName(pstmt uintptr, i int) (string, error) {
+ var p uintptr
+ _, err := s.FFI1(
+ bindParameterName,
+ virtual.PtrResult{&p},
+ virtual.Ptr(pstmt),
+ virtual.Int32(i),
+ )
+ return virtual.GoString(p), err
+}
+
+// int sqlite3_finalize(sqlite3_stmt *pStmt);
+func (s *stmt) finalize(pstmt uintptr) error {
+ var rc int32
+ if _, err := s.FFI1(
+ finalize,
+ virtual.Int32Result{&rc},
+ virtual.Ptr(pstmt),
+ ); err != nil {
+ return err
+ }
+
+ if rc != bin.XSQLITE_OK {
+ return s.errstr(rc)
+ }
+
+ return nil
+}
+
+// int sqlite3_step(sqlite3_stmt*);
+func (s *stmt) step(pstmt uintptr) (int, error) {
+ var rc int32
+ _, err := s.FFI1(
+ step,
+ virtual.Int32Result{&rc},
+ virtual.Ptr(pstmt),
+ )
+ return int(rc), err
+}
+
+// int sqlite3_prepare_v2(
+// sqlite3 *db, /* Database handle */
+// const char *zSql, /* SQL statement, UTF-8 encoded */
+// int nByte, /* Maximum length of zSql in bytes. */
+// sqlite3_stmt **ppStmt, /* OUT: Statement handle */
+// const char **pzTail /* OUT: Pointer to unused portion of zSql */
+// );
+func (s *stmt) prepareV2(zSql uintptr) error {
+ var rc int32
+ if _, err := s.FFI1(
+ prepareV2,
+ virtual.Int32Result{&rc},
+ virtual.Ptr(s.pdb()), virtual.Ptr(zSql), virtual.Int32(-1), virtual.Ptr(s.ppstmt), virtual.Ptr(s.pzTail),
+ ); err != nil {
+ return err
+ }
+
+ if rc != bin.XSQLITE_OK {
+ return s.errstr(rc)
+ }
+
+ return nil
+}
+
+type tx struct {
+ *conn
+}
+
+func (t *tx) String() string { return fmt.Sprintf("&%T@%p{conn: %p}", *t, t, t.conn) }
+
+func newTx(c *conn) (*tx, error) {
+ t := &tx{conn: c}
+ if err := t.exec(context.Background(), "begin"); err != nil {
+ return nil, err
+ }
+
+ return t, nil
+}
+
+// Commit implements driver.Tx.
+func (t *tx) Commit() (err error) {
+ if trace {
+ defer func() {
+ tracer(t, "Commit(): %v", err)
+ }()
+ }
+ return t.exec(context.Background(), "commit")
+}
+
+// Rollback implements driver.Tx.
+func (t *tx) Rollback() (err error) {
+ if trace {
+ defer func() {
+ tracer(t, "Rollback(): %v", err)
+ }()
+ }
+ return t.exec(context.Background(), "rollback")
+}
+
+// int sqlite3_exec(
+// sqlite3*, /* An open database */
+// const char *sql, /* SQL to be evaluated */
+// int (*callback)(void*,int,char**,char**), /* Callback function */
+// void *, /* 1st argument to callback */
+// char **errmsg /* Error msg written here */
+// );
+func (t *tx) exec(ctx context.Context, sql string) (err error) {
+ psql, err := t.cString(sql)
+ if err != nil {
+ return err
+ }
+
+ defer t.free(psql)
+
+ // TODO: use t.conn.ExecContext() instead
+ donech := make(chan struct{})
+ defer close(donech)
+ go func() {
+ select {
+ case <-ctx.Done():
+ t.interrupt(t.pdb())
+ case <-donech:
+ }
+ }()
+
+ var rc int32
+ if _, err = t.FFI1(
+ exec,
+ virtual.Int32Result{&rc},
+ virtual.Ptr(t.pdb()), virtual.Ptr(psql), null, null, null,
+ ); err != nil {
+ return err
+ }
+
+ if rc != bin.XSQLITE_OK {
+ return t.errstr(rc)
+ }
+
+ return nil
+}
+
+type conn struct {
+ *Driver
+ *virtual.Thread
+ ppdb uintptr
+}
+
+func (c *conn) String() string {
+ return fmt.Sprintf("&%T@%p{sqlite: %p, Thread: %p, ppdb: %#x}", *c, c, c.Driver, c.Thread, c.ppdb)
+}
+
+func newConn(s *Driver, name string) (_ *conn, err error) {
+ c := &conn{Driver: s}
+
+ defer func() {
+ if err != nil {
+ c.close()
+ }
+ }()
+
+ c.Lock()
+
+ defer c.Unlock()
+
+ c.conns++
+ if c.conns == 1 {
+ stderr := ioutil.Discard
+ if trace {
+ stderr = debugWriter{}
+ }
+ m, status, err2 := virtual.New(&binary, []string{"", fmt.Sprint(vmHeapSize - vmHeapReserve)}, nil, nil, stderr, vmHeapSize, vmMainStackSize, "")
+ if status != 0 || err2 != nil {
+ return nil, fmt.Errorf("virtual.New: %v, %v", status, err2)
+ }
+
+ vm = m
+ }
+ c.Thread, err = vm.NewThread(vmStackSize)
+ if err != nil {
+ return nil, err
+ }
+
+ if err = c.openV2(
+ name,
+ bin.XSQLITE_OPEN_READWRITE|bin.XSQLITE_OPEN_CREATE|
+ bin.XSQLITE_OPEN_FULLMUTEX|
+ bin.XSQLITE_OPEN_URI,
+ ); err != nil {
+ return nil, err
+ }
+
+ if err = c.extendedResultCodes(true); err != nil {
+ return nil, err
+ }
+
+ return c, nil
+}
+
+// Prepare returns a prepared statement, bound to this connection.
+func (c *conn) Prepare(query string) (s driver.Stmt, err error) {
+ return c.prepare(context.Background(), query)
+}
+
+func (c *conn) prepare(ctx context.Context, query string) (s driver.Stmt, err error) {
+ if trace {
+ defer func() {
+ tracer(c, "Prepare(%s): (%v, %v)", query, s, err)
+ }()
+ }
+ return newStmt(c, query)
+}
+
+// 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 *conn) Close() (err error) {
+ if trace {
+ defer func() {
+ tracer(c, "Close(): %v", err)
+ }()
+ }
+ return c.close()
+}
+
+// Begin starts a transaction.
+func (c *conn) Begin() (driver.Tx, error) {
+ return c.begin(context.Background(), txOptions{})
+}
+
+// copy of driver.TxOptions
+type txOptions struct {
+ Isolation int // driver.IsolationLevel
+ ReadOnly bool
+}
+
+func (c *conn) begin(ctx context.Context, opts txOptions) (t driver.Tx, err error) {
+ if trace {
+ defer func() {
+ tracer(c, "BeginTx(): (%v, %v)", t, err)
+ }()
+ }
+ return newTx(c)
+}
+
+// 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 ErrSkip.
+func (c *conn) Exec(query string, args []driver.Value) (driver.Result, error) {
+ return c.exec(context.Background(), query, toNamedValues(args))
+}
+
+func (c *conn) exec(ctx context.Context, query string, args []namedValue) (r driver.Result, err error) {
+ if trace {
+ defer func() {
+ tracer(c, "ExecContext(%s, %v): (%v, %v)", query, args, r, err)
+ }()
+ }
+
+ s, err := c.prepare(ctx, query)
+ if err != nil {
+ return nil, err
+ }
+
+ defer func() {
+ if err2 := s.Close(); err2 != nil && err == nil {
+ err = err2
+ }
+ }()
+
+ return s.(*stmt).exec(ctx, args)
+}
+
+// copy of driver.NameValue
+type namedValue struct {
+ Name string
+ Ordinal int
+ Value driver.Value
+}
+
+// toNamedValues converts []driver.Value to []namedValue
+func toNamedValues(vals []driver.Value) []namedValue {
+ args := make([]namedValue, 0, len(vals))
+ for i, val := range vals {
+ args = append(args, namedValue{Value: val, Ordinal: i + 1})
+ }
+ return args
+}
+
+// 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 ErrSkip.
+func (c *conn) Query(query string, args []driver.Value) (driver.Rows, error) {
+ return c.query(context.Background(), query, toNamedValues(args))
+}
+
+func (c *conn) query(ctx context.Context, query string, args []namedValue) (r driver.Rows, err error) {
+ if trace {
+ defer func() {
+ tracer(c, "Query(%s, %v): (%v, %v)", query, args, r, err)
+ }()
+ }
+ s, err := c.prepare(ctx, query)
+ if err != nil {
+ return nil, err
+ }
+
+ defer func() {
+ if err2 := s.Close(); err2 != nil && err == nil {
+ err = err2
+ }
+ }()
+
+ return s.(*stmt).query(ctx, args)
+}
+
+func (c *conn) pdb() uintptr { return readPtr(c.ppdb) }
+
+// int sqlite3_extended_result_codes(sqlite3*, int onoff);
+func (c *conn) extendedResultCodes(on bool) (err error) {
+ var v, rc int32
+ if on {
+ v = 1
+ }
+ if _, err = c.FFI1(
+ extendedResultCodes,
+ virtual.Int32Result{&rc},
+ virtual.Ptr(c.pdb()), virtual.Int32(v),
+ ); err != nil {
+ return err
+ }
+
+ if rc != bin.XSQLITE_OK {
+ return c.errstr(rc)
+ }
+
+ return nil
+}
+
+// void *sqlite3_malloc(int);
+func (c *conn) malloc(n int) (r uintptr, err error) {
+ _, err = c.FFI1(
+ maloc,
+ virtual.PtrResult{&r},
+ virtual.Int32(int32(n)),
+ )
+ return r, err
+}
+
+func (c *conn) cString(s string) (p uintptr, err error) {
+ n := len(s)
+ if p, err = c.malloc(n + 1); err != nil {
+ return 0, err
+ }
+
+ virtual.CopyString(p, s, true)
+ return p, nil
+}
+
+// int sqlite3_open_v2(
+// const char *filename, /* Database filename (UTF-8) */
+// sqlite3 **ppDb, /* OUT: SQLite db handle */
+// int flags, /* Flags */
+// const char *zVfs /* Name of VFS module to use */
+// );
+func (c *conn) openV2(name string, flags int32) error {
+ filename, err := c.cString(name)
+ if err != nil {
+ return err
+ }
+
+ defer c.free(filename)
+
+ ppdb, err := c.malloc(ptrSize)
+ if err != nil {
+ return err
+ }
+
+ c.ppdb = ppdb
+ var rc int32
+ if _, err = c.FFI1(
+ openV2,
+ virtual.Int32Result{&rc},
+ virtual.Ptr(filename), virtual.Ptr(ppdb), virtual.Int32(flags), null,
+ ); err != nil {
+ return err
+ }
+
+ if rc != bin.XSQLITE_OK {
+ return c.errstr(rc)
+ }
+
+ return nil
+}
+
+// const char *sqlite3_errstr(int);
+func (c *conn) errstr(rc int32) (err error) {
+ var p uintptr
+ if _, err = c.FFI1(
+ errstr,
+ virtual.PtrResult{&p},
+ virtual.Int32(rc),
+ ); err != nil {
+ return err
+ }
+
+ str := virtual.GoString(p)
+ if _, err = c.FFI1(
+ errmsg,
+ virtual.PtrResult{&p},
+ virtual.Ptr(c.pdb()),
+ ); err != nil {
+ return err
+ }
+
+ switch msg := virtual.GoString(p); {
+ case msg == str:
+ return fmt.Errorf("%s (%v)", str, rc)
+ default:
+ return fmt.Errorf("%s: %s (%v)", str, msg, rc)
+ }
+}
+
+// int sqlite3_close_v2(sqlite3*);
+func (c *conn) closeV2() (err error) {
+ var rc int32
+ if _, err = c.FFI1(
+ closeV2,
+ virtual.Int32Result{&rc},
+ virtual.Ptr(c.pdb()),
+ ); err != nil {
+ return err
+ }
+
+ if rc != bin.XSQLITE_OK {
+ return c.errstr(rc)
+ }
+
+ err = c.free(c.ppdb)
+ c.ppdb = 0
+ return err
+}
+
+// void sqlite3_free(void*);
+func (c *conn) free(p uintptr) (err error) {
+ _, err = c.FFI0(
+ free,
+ virtual.Ptr(p),
+ )
+ return err
+}
+
+// void sqlite3_interrupt(sqlite3*);
+func (c *conn) interrupt(pdb uintptr) (err error) {
+ _, err = c.FFI0(
+ interrupt,
+ virtual.Ptr(pdb),
+ )
+ return err
+}
+
+func (c *conn) close() (err error) {
+ c.Lock()
+
+ defer func() {
+ c.conns--
+ if c.conns == 0 {
+ if err2 := vm.Close(); err2 != nil && err == nil {
+ err = err2
+ }
+ vm = nil
+ }
+ c.Unlock()
+ }()
+
+ if c.ppdb != 0 {
+ err = c.closeV2()
+ }
+ return err
+}
+
+type Driver struct {
+ conns int
+ sync.Mutex
+}
+
+func newDrv() *Driver { return &Driver{} }
+
+// 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.
+func (s *Driver) Open(name string) (c driver.Conn, err error) {
+ if trace {
+ defer func() {
+ tracer(s, "Open(%s): (%v, %v)", name, c, err)
+ }()
+ }
+ return newConn(s, name)
+}