diff options
Diffstat (limited to 'sqlite.go')
-rw-r--r-- | sqlite.go | 1305 |
1 files changed, 1305 insertions, 0 deletions
diff --git a/sqlite.go b/sqlite.go new file mode 100644 index 0000000..5f39070 --- /dev/null +++ b/sqlite.go @@ -0,0 +1,1305 @@ +// 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. + +//TODO Pinger (tags go1.8) + +//go:generate go run generator.go + +// Package sqlite is an in-process implementation of a self-contained, +// serverless, zero-configuration, transactional SQL database engine. (Work In Progress) +// +// Do not use in production +// +// This is an experimental, pre-alpha, technology preview package. Performance +// is not (yet) a priority. When this virtual machine approach, hopefully, +// reaches a reasonable levels of completeness and correctness, the plan is to +// eventually mechanically translate the IR form, produced by +// http://github.com/cznic/ccir, to Go. Unreadable Go, presumably. +// +// Supported platforms and architectures +// +// See http://github.com/cznic/ccir. To add a newly supported os/arch +// combination to this package try running 'go generate'. +// +// Sqlite documentation +// +// See https://sqlite.org/docs.html +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" +) + +var ( + _ driver.Conn = (*conn)(nil) + _ driver.Driver = (*sqlite)(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 + 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 + 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"}, + {&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"}, + {&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.db()), + ) + 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.db()), + ) + 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 + step bool +} + +func (r *rows) String() string { + return fmt.Sprintf("&%T@%p{stmt: %p, columns: %v, rc0: %v, pstmt: %#x, step: %v}", *r, r, r.stmt, r.columns, r.rc0, r.pstmt, r.step) +} + +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.step { + if rc, err = r.stmt.step(r.pstmt); err != nil { + return err + } + } + + r.step = 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 + pquery uintptr + pstmt uintptr + pzTail uintptr +} + +func (s *stmt) String() string { + return fmt.Sprintf("&%T@%p{conn: %p, alloc %v, pquery: %#x, pstmt: %#x, pzTail: %#x}", *s, s, s.conn, s.allocs, s.pquery, s.pstmt, s.pzTail) +} + +func newStmt(c *conn, query string) (*stmt, error) { + s := &stmt{conn: c} + pquery, err := s.cString(query) + if err != nil { + return nil, err + } + + s.pquery = pquery + pstmt, err := s.malloc(ptrSize) + if err != nil { + s.free(pquery) + return nil, err + } + + s.pstmt = pstmt + pzTail, err := s.malloc(ptrSize) + if err != nil { + s.free(pquery) + s.free(pstmt) + 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.pquery != 0 { + err = s.free(s.pquery) + s.pquery = 0 + } + if s.pstmt != 0 { + if err2 := s.free(s.pstmt); err2 != nil && err == nil { + err = err2 + } + s.pstmt = 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. +// +// Deprecated: Drivers should implement StmtExecContext instead (or +// additionally). +func (s *stmt) Exec(args []driver.Value) (r driver.Result, err error) { + if trace { + defer func(args []driver.Value) { + tracer(s, "Exec(%v): (%v, %v)", args, r, err) + }(args) + } + for psql := s.pquery; readI8(psql) != 0; psql = readPtr(s.pzTail) { + if err := s.prepareV2(psql); err != nil { + return nil, err + } + + stmt := readPtr(s.pstmt) + if stmt == 0 { + continue + } + + n, err := s.bindParameterCount(stmt) + if err != nil { + return nil, err + } + + if n != 0 { + if err = s.bind(stmt, n, args); err != nil { + return nil, err + } + + args = args[n:] + } + rc, err := s.step(stmt) + if err != nil { + s.finalize(stmt) + return nil, err + } + + switch rc & 0xff { + case bin.XSQLITE_DONE, bin.XSQLITE_ROW: + if err := s.finalize(stmt); err != nil { + return nil, err + } + default: + err = s.errstr(int32(rc)) + s.finalize(stmt) + return nil, err + } + } + return newResult(s) +} + +// Query executes a query that may return rows, such as a SELECT. +// +// Deprecated: Drivers should implement StmtQueryContext instead (or +// additionally). +func (s *stmt) Query(args []driver.Value) (r driver.Rows, err error) { + if trace { + defer func(args []driver.Value) { + tracer(s, "Query(%v): (%v, %v)", args, r, err) + }(args) + } + var rowStmt uintptr + var rc0 int + for psql := s.pquery; readI8(psql) != 0; psql = readPtr(s.pzTail) { + if err := s.prepareV2(psql); err != nil { + return nil, err + } + + stmt := readPtr(s.pstmt) + if stmt == 0 { + continue + } + + n, err := s.bindParameterCount(stmt) + if err != nil { + return nil, err + } + + if n != 0 { + if err = s.bind(stmt, n, args); err != nil { + return nil, err + } + + args = args[n:] + } + rc, err := s.step(stmt) + if err != nil { + s.finalize(stmt) + return nil, err + } + + switch rc { + case bin.XSQLITE_ROW: + if rowStmt != 0 { + if err := s.finalize(stmt); err != nil { + return nil, err + } + + return nil, fmt.Errorf("query contains multiple select statements") + } + + rowStmt = stmt + rc0 = rc + case bin.XSQLITE_DONE: + if rowStmt == 0 { + rc0 = rc + } + default: + err = s.errstr(int32(rc)) + s.finalize(stmt) + return nil, err + } + } + return newRows(s, rowStmt, rc0) +} + +// int sqlite3_bind_double(sqlite3_stmt*, int, double); +func (s *stmt) bindDouble(stmt uintptr, idx1 int, value float64) (err error) { + var rc int32 + if _, err = s.FFI1( + bindDouble, + virtual.Int32Result{&rc}, + virtual.Ptr(stmt), 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(stmt uintptr, idx1, value int) (err error) { + var rc int32 + if _, err = s.FFI1( + bindInt, + virtual.Int32Result{&rc}, + virtual.Ptr(stmt), 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(stmt uintptr, idx1 int, value int64) (err error) { + var rc int32 + if _, err = s.FFI1( + bindInt64, + virtual.Int32Result{&rc}, + virtual.Ptr(stmt), 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(stmt 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(stmt), 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(stmt 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(stmt), 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(stmt uintptr, n int, args []driver.Value) error { + if len(args) < n { + return fmt.Errorf("missing arguments: got %v, expected %v", len(args), n) + } + + for i, v := range args[:n] { + switch x := v.(type) { + case int64: + if err := s.bindInt64(stmt, i+1, x); err != nil { + return err + } + case float64: + if err := s.bindDouble(stmt, i+1, x); err != nil { + return err + } + case bool: + v := 0 + if x { + v = 1 + } + if err := s.bindInt(stmt, i+1, v); err != nil { + return err + } + case []byte: + if err := s.bindBlob(stmt, i+1, x); err != nil { + return err + } + case string: + if err := s.bindText(stmt, i+1, x); err != nil { + return err + } + case time.Time: + if err := s.bindText(stmt, i+1, 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(stmt uintptr) (_ int, err error) { + var r int32 + _, err = s.FFI1( + bindParameterCount, + virtual.Int32Result{&r}, + virtual.Ptr(stmt), + ) + return int(r), err +} + +// int sqlite3_finalize(sqlite3_stmt *pStmt); +func (s *stmt) finalize(stmt uintptr) error { + var rc int32 + if _, err := s.FFI1( + finalize, + virtual.Int32Result{&rc}, + virtual.Ptr(stmt), + ); err != nil { + return err + } + + if rc != bin.XSQLITE_OK { + return s.errstr(rc) + } + + return nil +} + +// int sqlite3_step(sqlite3_stmt*); +func (s *stmt) step(stmt uintptr) (int, error) { + var rc int32 + _, err := s.FFI1( + step, + virtual.Int32Result{&rc}, + virtual.Ptr(stmt), + ) + 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.db()), virtual.Ptr(zSql), virtual.Int32(-1), virtual.Ptr(s.pstmt), 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("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("commit") +} + +// Rollback implements driver.Tx. +func (t *tx) Rollback() (err error) { + if trace { + defer func() { + tracer(t, "Rollback(): %v", err) + }() + } + return t.exec("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(stmt string) (err error) { + sql, err := t.cString(stmt) + if err != nil { + return err + } + + defer t.free(sql) + + var rc int32 + if _, err = t.FFI1( + exec, + virtual.Int32Result{&rc}, + virtual.Ptr(t.db()), virtual.Ptr(sql), null, null, null, + ); err != nil { + return err + } + + if rc != bin.XSQLITE_OK { + return t.errstr(rc) + } + + return nil +} + +type conn struct { + *sqlite + *virtual.Thread + pdb uintptr +} + +func (c *conn) String() string { + return fmt.Sprintf("&%T@%p{sqlite: %p, Thread: %p, pdb: %#x}", *c, c, c.sqlite, c.Thread, c.pdb) +} + +func newConn(s *sqlite, name string) (_ *conn, err error) { + c := &conn{sqlite: 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) { + 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 and returns a new transaction. +// +// Deprecated: Drivers should implement ConnBeginTx instead (or additionally). +func (c *conn) Begin() (t driver.Tx, err error) { + if trace { + defer func() { + tracer(c, "Begin(): (%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. +// +// Deprecated: Drivers should implement ExecerContext instead (or +// additionally). +func (c *conn) Exec(query string, args []driver.Value) (r driver.Result, err error) { + if trace { + defer func() { + tracer(c, "Exec(%s, %v): (%v, %v)", query, args, r, err) + }() + } + s, err := c.Prepare(query) + if err != nil { + return nil, err + } + + defer func() { + if err2 := s.Close(); err2 != nil && err == nil { + err = err2 + } + }() + + return s.Exec(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. +// +// Deprecated: Drivers should implement QueryerContext instead (or +// additionally). +func (c *conn) Query(query string, args []driver.Value) (r driver.Rows, err error) { + if trace { + defer func() { + tracer(c, "Query(%s, %v): (%v, %v)", query, args, r, err) + }() + } + s, err := c.Prepare(query) + if err != nil { + return nil, err + } + + defer func() { + if err2 := s.Close(); err2 != nil && err == nil { + err = err2 + } + }() + + return s.Query(args) +} + +func (c *conn) db() uintptr { return readPtr(c.pdb) } + +// 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.db()), 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) + + pdb, err := c.malloc(ptrSize) + if err != nil { + return err + } + + c.pdb = pdb + var rc int32 + if _, err = c.FFI1( + openV2, + virtual.Int32Result{&rc}, + virtual.Ptr(filename), virtual.Ptr(pdb), 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.db()), + ); 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.db()), + ); err != nil { + return err + } + + if rc != bin.XSQLITE_OK { + return c.errstr(rc) + } + + err = c.free(c.pdb) + c.pdb = 0 + return err +} + +// void sqlite3_free(void*); +func (c *conn) free(p uintptr) (err error) { + _, err = c.FFI0( + free, + virtual.Ptr(p), + ) + 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.pdb != 0 { + err = c.closeV2() + } + return err +} + +type sqlite struct { + conns int + sync.Mutex +} + +func newDrv() *sqlite { return &sqlite{} } + +// 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 *sqlite) 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) +} |