diff options
author | Steffen <steffen.butzer@outlook.com> | 2017-06-10 14:02:32 +0200 |
---|---|---|
committer | Steffen <steffen.butzer@outlook.com> | 2017-06-10 14:02:32 +0200 |
commit | 6b30b8701efe4d6bd16ebae7f8f2f5fee60462c4 (patch) | |
tree | a57f4feb44a0b067766504bb208c85b55847520b /sqlite_windows.go | |
parent | 212e4e19afc9ea6da37ed1357839fa16e6f8dcb1 (diff) |
switch windows to CRT, throw out old virtual
Diffstat (limited to 'sqlite_windows.go')
-rw-r--r-- | sqlite_windows.go | 1406 |
1 files changed, 0 insertions, 1406 deletions
diff --git a/sqlite_windows.go b/sqlite_windows.go deleted file mode 100644 index 4d289d3..0000000 --- a/sqlite_windows.go +++ /dev/null @@ -1,1406 +0,0 @@ -// 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) -} |