diff options
author | Jan Mercl <0xjnml@gmail.com> | 2019-12-31 16:46:08 +0100 |
---|---|---|
committer | Jan Mercl <0xjnml@gmail.com> | 2019-12-31 16:46:08 +0100 |
commit | ef38ac9c3be91d0b0c15bdcfa89e0469f7150ba0 (patch) | |
tree | 6917153245d6dceb223350fa0e37d73484dbaacc /sqlite.go | |
parent | ea2990f2041f1564cf05b1e6510bdae19f2f25ff (diff) |
use sqlite3_unlock_notify, fixes #20.
Diffstat (limited to 'sqlite.go')
-rw-r--r-- | sqlite.go | 1168 |
1 files changed, 636 insertions, 532 deletions
@@ -13,8 +13,6 @@ import ( "database/sql/driver" "fmt" "io" - "math" - "sync" "time" "unsafe" @@ -31,11 +29,79 @@ var ( _ driver.Rows = (*rows)(nil) _ driver.Stmt = (*stmt)(nil) _ driver.Tx = (*tx)(nil) + _ error = (*Error)(nil) ) const ( - driverName = "sqlite" - ptrSize = int(unsafe.Sizeof(uintptr(0))) + driverName = "sqlite" + ptrSize = int(unsafe.Sizeof(uintptr(0))) + sqliteLockedSharedcache = bin.DSQLITE_LOCKED | (1 << 8) +) + +// Error represents sqlite library error code. +type Error struct { + msg string + code int +} + +// Error implements error. +func (e *Error) Error() string { return e.msg } + +// Code returns the sqlite result code for this error. +func (e *Error) Code() int { return e.code } + +var ( + // ErrorCodeString maps Error.Code() to its string representation. + ErrorCodeString = map[int]string{ + bin.DSQLITE_ABORT: "Callback routine requested an abort (SQLITE_ABORT)", + bin.DSQLITE_AUTH: "Authorization denied (SQLITE_AUTH)", + bin.DSQLITE_BUSY: "The database file is locked (SQLITE_BUSY)", + bin.DSQLITE_CANTOPEN: "Unable to open the database file (SQLITE_CANTOPEN)", + bin.DSQLITE_CONSTRAINT: "Abort due to constraint violation (SQLITE_CONSTRAINT)", + bin.DSQLITE_CORRUPT: "The database disk image is malformed (SQLITE_CORRUPT)", + bin.DSQLITE_DONE: "sqlite3_step() has finished executing (SQLITE_DONE)", + bin.DSQLITE_EMPTY: "Internal use only (SQLITE_EMPTY)", + bin.DSQLITE_ERROR: "Generic error (SQLITE_ERROR)", + bin.DSQLITE_FORMAT: "Not used (SQLITE_FORMAT)", + bin.DSQLITE_FULL: "Insertion failed because database is full (SQLITE_FULL)", + bin.DSQLITE_INTERNAL: "Internal logic error in SQLite (SQLITE_INTERNAL)", + bin.DSQLITE_INTERRUPT: "Operation terminated by sqlite3_interrupt()(SQLITE_INTERRUPT)", + bin.DSQLITE_IOERR | (1 << 8): "(SQLITE_IOERR_READ)", + bin.DSQLITE_IOERR | (10 << 8): "(SQLITE_IOERR_DELETE)", + bin.DSQLITE_IOERR | (11 << 8): "(SQLITE_IOERR_BLOCKED)", + bin.DSQLITE_IOERR | (12 << 8): "(SQLITE_IOERR_NOMEM)", + bin.DSQLITE_IOERR | (13 << 8): "(SQLITE_IOERR_ACCESS)", + bin.DSQLITE_IOERR | (14 << 8): "(SQLITE_IOERR_CHECKRESERVEDLOCK)", + bin.DSQLITE_IOERR | (15 << 8): "(SQLITE_IOERR_LOCK)", + bin.DSQLITE_IOERR | (16 << 8): "(SQLITE_IOERR_CLOSE)", + bin.DSQLITE_IOERR | (17 << 8): "(SQLITE_IOERR_DIR_CLOSE)", + bin.DSQLITE_IOERR | (2 << 8): "(SQLITE_IOERR_SHORT_READ)", + bin.DSQLITE_IOERR | (3 << 8): "(SQLITE_IOERR_WRITE)", + bin.DSQLITE_IOERR | (4 << 8): "(SQLITE_IOERR_FSYNC)", + bin.DSQLITE_IOERR | (5 << 8): "(SQLITE_IOERR_DIR_FSYNC)", + bin.DSQLITE_IOERR | (6 << 8): "(SQLITE_IOERR_TRUNCATE)", + bin.DSQLITE_IOERR | (7 << 8): "(SQLITE_IOERR_FSTAT)", + bin.DSQLITE_IOERR | (8 << 8): "(SQLITE_IOERR_UNLOCK)", + bin.DSQLITE_IOERR | (9 << 8): "(SQLITE_IOERR_RDLOCK)", + bin.DSQLITE_IOERR: "Some kind of disk I/O error occurred (SQLITE_IOERR)", + bin.DSQLITE_LOCKED | (1 << 8): "(SQLITE_LOCKED_SHAREDCACHE)", + bin.DSQLITE_LOCKED: "A table in the database is locked (SQLITE_LOCKED)", + bin.DSQLITE_MISMATCH: "Data type mismatch (SQLITE_MISMATCH)", + bin.DSQLITE_MISUSE: "Library used incorrectly (SQLITE_MISUSE)", + bin.DSQLITE_NOLFS: "Uses OS features not supported on host (SQLITE_NOLFS)", + bin.DSQLITE_NOMEM: "A malloc() failed (SQLITE_NOMEM)", + bin.DSQLITE_NOTADB: "File opened that is not a database file (SQLITE_NOTADB)", + bin.DSQLITE_NOTFOUND: "Unknown opcode in sqlite3_file_control() (SQLITE_NOTFOUND)", + bin.DSQLITE_NOTICE: "Notifications from sqlite3_log() (SQLITE_NOTICE)", + bin.DSQLITE_PERM: "Access permission denied (SQLITE_PERM)", + bin.DSQLITE_PROTOCOL: "Database lock protocol error (SQLITE_PROTOCOL)", + bin.DSQLITE_RANGE: "2nd parameter to sqlite3_bind out of range (SQLITE_RANGE)", + bin.DSQLITE_READONLY: "Attempt to write a readonly database (SQLITE_READONLY)", + bin.DSQLITE_ROW: "sqlite3_step() has another row ready (SQLITE_ROW)", + bin.DSQLITE_SCHEMA: "The database schema changed (SQLITE_SCHEMA)", + bin.DSQLITE_TOOBIG: "String or BLOB exceeds size limit (SQLITE_TOOBIG)", + bin.DSQLITE_WARNING: "Warnings from sqlite3_log() (SQLITE_WARNING)", + } ) func init() { @@ -58,39 +124,27 @@ func init() { } crt.Xfree(tls, varArgs) - sql.Register(driverName, newDrv()) + sql.Register(driverName, newDriver()) } type result struct { - *stmt lastInsertID int64 rowsAffected int } -func newResult(s *stmt) (_ *result, err error) { - r := &result{stmt: s} - if r.rowsAffected, err = r.changes(); err != nil { +func newResult(c *conn) (_ *result, err error) { + r := &result{} + if r.rowsAffected, err = c.changes(); err != nil { return nil, err } - if r.lastInsertID, err = r.lastInsertRowID(); err != nil { + if r.lastInsertID, err = c.lastInsertRowID(); err != nil { return nil, err } return r, nil } -// sqlite3_int64 sqlite3_last_insert_rowid(sqlite3*); -func (r *result) lastInsertRowID() (v int64, _ error) { - return bin.Xsqlite3_last_insert_rowid(r.tls, r.pdb()), nil -} - -// int sqlite3_changes(sqlite3*); -func (r *result) changes() (int, error) { - v := bin.Xsqlite3_changes(r.tls, r.pdb()) - return int(v), nil -} - // 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) { @@ -111,29 +165,31 @@ func (r *result) RowsAffected() (int64, error) { } type rows struct { - *stmt + allocs []crt.Intptr + c *conn columns []string - rc0 int pstmt crt.Intptr - doStep bool + + doStep bool } -func newRows(s *stmt, pstmt crt.Intptr, rc0 int) (*rows, error) { - s.owned = true - r := &rows{ - stmt: s, - pstmt: pstmt, - rc0: rc0, - } +func newRows(c *conn, pstmt crt.Intptr, allocs []crt.Intptr) (r *rows, err error) { + defer func() { + if err != nil { + c.finalize(pstmt) + r = nil + } + }() - n, err := r.columnCount() + r = &rows{c: c, pstmt: pstmt, allocs: allocs} + n, err := c.columnCount(pstmt) 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 { + if r.columns[i], err = r.c.columnName(pstmt, i); err != nil { return nil, err } } @@ -141,6 +197,15 @@ func newRows(s *stmt, pstmt crt.Intptr, rc0 int) (*rows, error) { return r, nil } +// Close closes the rows iterator. +func (r *rows) Close() (err error) { + for _, v := range r.allocs { + r.c.free(v) + } + r.allocs = nil + return r.c.finalize(r.pstmt) +} + // 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. @@ -148,66 +213,55 @@ func (r *rows) Columns() (c []string) { return r.columns } -// Close closes the rows iterator. -func (r *rows) Close() (err error) { - err = r.finalize(r.pstmt) - r.stmt.owned = false - if err2 := r.stmt.Close(); err2 != nil && err == nil { - err = err2 - } - return err -} - // 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) { - rc := r.rc0 + rc := bin.DSQLITE_ROW if r.doStep { - if rc, err = r.step(r.pstmt); err != nil { + if rc, err = r.c.step(r.pstmt); err != nil { return err } } r.doStep = true - switch rc { case bin.DSQLITE_ROW: if g, e := len(dest), len(r.columns); g != e { - return fmt.Errorf("Next(): have %v destination values, expected %v", g, e) + return fmt.Errorf("sqlite: Next: have %v destination values, expected %v", g, e) } for i := range dest { - ct, err := r.columnType(i) + ct, err := r.c.columnType(r.pstmt, i) if err != nil { return err } switch ct { case bin.DSQLITE_INTEGER: - v, err := r.columnInt64(i) + v, err := r.c.columnInt64(r.pstmt, i) if err != nil { return err } dest[i] = v case bin.DSQLITE_FLOAT: - v, err := r.columnDouble(i) + v, err := r.c.columnDouble(r.pstmt, i) if err != nil { return err } dest[i] = v case bin.DSQLITE_TEXT: - v, err := r.columnText(i) + v, err := r.c.columnText(r.pstmt, i) if err != nil { return err } dest[i] = v case bin.DSQLITE_BLOB: - v, err := r.columnBlob(i) + v, err := r.c.columnBlob(r.pstmt, i) if err != nil { return err } @@ -223,147 +277,125 @@ func (r *rows) Next(dest []driver.Value) (err error) { case bin.DSQLITE_DONE: return io.EOF default: - return r.errstr(int32(rc)) + return r.c.errstr(int32(rc)) } } -// int sqlite3_column_bytes(sqlite3_stmt*, int iCol); -func (r *rows) columnBytes(iCol int) (_ int, err error) { - v := bin.Xsqlite3_column_bytes(r.tls, r.pstmt, int32(iCol)) - return int(v), nil +type stmt struct { + c *conn + psql crt.Intptr } -// const void *sqlite3_column_blob(sqlite3_stmt*, int iCol); -func (r *rows) columnBlob(iCol int) (v []byte, err error) { - p := bin.Xsqlite3_column_blob(r.tls, r.pstmt, int32(iCol)) - len, err := r.columnBytes(iCol) +func newStmt(c *conn, sql string) (*stmt, error) { + p, err := crt.CString(sql) if err != nil { return nil, err } - if p == 0 || len == 0 { - return nil, nil - } + return &stmt{c: c, psql: p}, nil +} - v = make([]byte, len) - copy(v, (*crt.RawMem)(unsafe.Pointer(uintptr(p)))[:len]) - return v, 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) { + s.c.free(s.psql) + s.psql = 0 + return nil } -// const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol); -func (r *rows) columnText(iCol int) (v string, err error) { - p := bin.Xsqlite3_column_text(r.tls, r.pstmt, int32(iCol)) - len, err := r.columnBytes(iCol) - if err != nil { - return "", err - } +// 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) (driver.Result, error) { //TODO StmtExecContext + return s.exec(context.Background(), toNamedValues(args)) +} - if p == 0 || len == 0 { - return "", nil +// toNamedValues converts []driver.Value to []driver.NamedValue +func toNamedValues(vals []driver.Value) (r []driver.NamedValue) { + r = make([]driver.NamedValue, len(vals)) + for i, val := range vals { + r[i] = driver.NamedValue{Value: val, Ordinal: i + 1} } - - b := make([]byte, len) - copy(b, (*crt.RawMem)(unsafe.Pointer(uintptr(p)))[:len]) - return string(b), nil + return r } -// double sqlite3_column_double(sqlite3_stmt*, int iCol); -func (r *rows) columnDouble(iCol int) (v float64, err error) { - v = bin.Xsqlite3_column_double(r.tls, r.pstmt, int32(iCol)) - return v, nil -} +func (s *stmt) exec(ctx context.Context, args []driver.NamedValue) (r driver.Result, err error) { + var pstmt crt.Intptr -// sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol); -func (r *rows) columnInt64(iCol int) (v int64, err error) { - v = bin.Xsqlite3_column_int64(r.tls, r.pstmt, int32(iCol)) - return v, nil -} + donech := make(chan struct{}) -// int sqlite3_column_type(sqlite3_stmt*, int iCol); -func (r *rows) columnType(iCol int) (_ int, err error) { - v := bin.Xsqlite3_column_type(r.tls, r.pstmt, int32(iCol)) - return int(v), nil -} + go func() { + select { + case <-ctx.Done(): + if pstmt != 0 { + s.c.interrupt(s.c.db) + } + case <-donech: + } + }() -// int sqlite3_column_count(sqlite3_stmt *pStmt); -func (r *rows) columnCount() (_ int, err error) { - v := bin.Xsqlite3_column_count(r.tls, r.pstmt) - return int(v), nil -} + defer func() { + pstmt = 0 + close(donech) + }() -// const char *sqlite3_column_name(sqlite3_stmt*, int N); -func (r *rows) columnName(n int) (string, error) { - p := bin.Xsqlite3_column_name(r.tls, r.pstmt, int32(n)) - return crt.GoString(p), nil -} + for psql := s.psql; *(*byte)(unsafe.Pointer(uintptr(psql))) != 0; { + if pstmt, err = s.c.prepareV2(&psql); err != nil { + return nil, err + } -type stmt struct { - *conn - allocs []crt.Intptr - psql crt.Intptr // *int8 - ppstmt crt.Intptr // **sqlite3_stmt - pzTail crt.Intptr // **int8 + if pstmt == 0 { + continue + } - owned bool -} + if err := func() (err error) { + defer func() { + if e := s.c.finalize(pstmt); e != nil && err == nil { + err = e + } + }() -func newStmt(c *conn, sql string) (*stmt, error) { - s := &stmt{conn: c} - psql, err := s.cString(sql) - if err != nil { - return nil, err - } + n, err := s.c.bindParameterCount(pstmt) + if err != nil { + return err + } - s.psql = psql - ppstmt, err := s.malloc(ptrSize) - if err != nil { - s.free(psql) - return nil, err - } + if n != 0 { + allocs, err := s.c.bind(pstmt, n, args) + if err != nil { + return err + } - s.ppstmt = ppstmt - pzTail, err := s.malloc(ptrSize) - if err != nil { - s.free(psql) - s.free(ppstmt) - return nil, err - } + if len(allocs) != 0 { + defer func() { + for _, v := range allocs { + s.c.free(v) + } + }() + } + } - s.pzTail = pzTail - return s, nil -} + rc, err := s.c.step(pstmt) + if err != nil { + return err + } -// 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 s.owned { - return - } + switch rc & 0xff { + case bin.DSQLITE_DONE, bin.DSQLITE_ROW: + // nop + default: + return s.c.errstr(int32(rc)) + } - 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 + return nil + }(); err != nil { + return nil, err } } - s.allocs = nil - return err + return newResult(s.c) } // NumInput returns the number of placeholder parameters. @@ -379,205 +411,324 @@ func (s *stmt) NumInput() (n int) { 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)) +// 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) (driver.Rows, error) { //TODO StmtQueryContext + return s.query(context.Background(), toNamedValues(args)) } -func (s *stmt) exec(ctx context.Context, args []namedValue) (r driver.Result, err error) { +func (s *stmt) query(ctx context.Context, args []driver.NamedValue) (r driver.Rows, err error) { var pstmt crt.Intptr donech := make(chan struct{}) - defer close(donech) + go func() { select { case <-ctx.Done(): if pstmt != 0 { - s.interrupt(s.pdb()) + s.c.interrupt(s.c.db) } case <-donech: } }() - for psql := s.psql; *(*byte)(unsafe.Pointer(uintptr(psql))) != 0; psql = *(*crt.Intptr)(unsafe.Pointer(uintptr(s.pzTail))) { - if err := s.prepareV2(psql); err != nil { + defer func() { + pstmt = 0 + close(donech) + }() + + var allocs []crt.Intptr + for psql := s.psql; *(*byte)(unsafe.Pointer(uintptr(psql))) != 0; { + if pstmt, err = s.c.prepareV2(&psql); err != nil { return nil, err } - pstmt = *(*crt.Intptr)(unsafe.Pointer(uintptr(s.ppstmt))) if pstmt == 0 { continue } - n, err := s.bindParameterCount(pstmt) - if err != nil { - return nil, err - } + if err = func() (err error) { + defer func() { + if e := s.c.finalize(pstmt); e != nil && err == nil { + err = e + } + }() - if n != 0 { - if err = s.bind(pstmt, n, args); err != nil { - return nil, err + n, err := s.c.bindParameterCount(pstmt) + if err != nil { + return err } - } - rc, err := s.step(pstmt) - if err != nil { - s.finalize(pstmt) - return nil, err - } + if n != 0 { + a, err := s.c.bind(pstmt, n, args) + if err != nil { + return err + } - switch rc & 0xff { - case bin.DSQLITE_DONE, bin.DSQLITE_ROW: - if err := s.finalize(pstmt); err != nil { - return nil, err + if len(a) != 0 { + allocs = append(allocs, a...) + } } - default: - err = s.errstr(int32(rc)) - s.finalize(pstmt) + + rc, err := s.c.step(pstmt) + if err != nil { + return err + } + + switch rc & 0xff { + case bin.DSQLITE_ROW: + if r, err = newRows(s.c, pstmt, allocs); err != nil { + return err + } + + pstmt = 0 + return nil + case bin.DSQLITE_DONE: + // nop + default: + return s.c.errstr(int32(rc)) + } + + return nil + }(); err != nil { return nil, err } } - return newResult(s) + if r != nil { + return r, nil + } + + panic("TODO") +} + +type tx struct { + c *conn +} + +func newTx(c *conn) (*tx, error) { + r := &tx{c: c} + if err := r.exec(context.Background(), "begin"); err != nil { + return nil, err + } + + return r, nil } -func (s *stmt) Query(args []driver.Value) (driver.Rows, error) { - return s.query(context.Background(), toNamedValues(args)) +// Commit implements driver.Tx. +func (t *tx) Commit() (err error) { + return t.exec(context.Background(), "commit") } -func (s *stmt) query(ctx context.Context, args []namedValue) (r driver.Rows, err error) { - var pstmt, rowStmt crt.Intptr - var rc0 int +// Rollback implements driver.Tx. +func (t *tx) Rollback() (err error) { + return t.exec(context.Background(), "rollback") +} +func (t *tx) exec(ctx context.Context, sql string) (err error) { + psql, err := crt.CString(sql) + if err != nil { + return err + } + + defer t.c.free(psql) + + //TODO use t.conn.ExecContext() instead donech := make(chan struct{}) + defer close(donech) + go func() { select { case <-ctx.Done(): - if pstmt != 0 { - s.interrupt(s.pdb()) - } + t.c.interrupt(t.c.db) case <-donech: } }() - for psql := s.psql; *(*byte)(unsafe.Pointer(uintptr(psql))) != 0; psql = *(*crt.Intptr)(unsafe.Pointer(uintptr(s.pzTail))) { - if err := s.prepareV2(psql); err != nil { - return nil, err - } + if rc := bin.Xsqlite3_exec(t.c.tls, t.c.db, psql, 0, 0, 0); rc != bin.DSQLITE_OK { + return t.c.errstr(rc) + } - pstmt = *(*crt.Intptr)(unsafe.Pointer(uintptr(s.ppstmt))) - if pstmt == 0 { - continue - } + return nil +} - n, err := s.bindParameterCount(pstmt) - if err != nil { - return nil, err - } +type conn struct { + db crt.Intptr // *bin.Xsqlite3 + tls *crt.TLS +} - if n != 0 { - if err = s.bind(pstmt, n, args); err != nil { - return nil, err - } - } +func newConn(name string) (*conn, error) { + c := &conn{tls: crt.NewTLS()} + db, err := c.openV2( + name, + bin.DSQLITE_OPEN_READWRITE|bin.DSQLITE_OPEN_CREATE| + bin.DSQLITE_OPEN_FULLMUTEX| + bin.DSQLITE_OPEN_URI, + ) + if err != nil { + return nil, err + } - rc, err := s.step(pstmt) - if err != nil { - s.finalize(pstmt) - return nil, err - } + c.db = db + if err = c.extendedResultCodes(true); err != nil { + return nil, err + } - switch rc { - case bin.DSQLITE_ROW: - if rowStmt != 0 { - if err := s.finalize(pstmt); err != nil { - return nil, err - } + return c, nil +} - return nil, fmt.Errorf("query contains multiple select statements") - } +// const void *sqlite3_column_blob(sqlite3_stmt*, int iCol); +func (c *conn) columnBlob(pstmt crt.Intptr, iCol int) (v []byte, err error) { + p := bin.Xsqlite3_column_blob(c.tls, pstmt, int32(iCol)) + len, err := c.columnBytes(pstmt, iCol) + if err != nil { + return nil, err + } - rowStmt = pstmt - rc0 = rc - case bin.DSQLITE_DONE: - if rowStmt == 0 { - rc0 = rc - } - default: - err = s.errstr(int32(rc)) - s.finalize(pstmt) - return nil, err - } + if p == 0 || len == 0 { + return nil, nil } - return newRows(s, rowStmt, rc0) + + v = make([]byte, len) + copy(v, (*crt.RawMem)(unsafe.Pointer(uintptr(p)))[:len]) + return v, nil } -// int sqlite3_bind_double(sqlite3_stmt*, int, double); -func (s *stmt) bindDouble(pstmt crt.Intptr, idx1 int, value float64) (err error) { - if rc := bin.Xsqlite3_bind_double(s.tls, pstmt, int32(idx1), value); rc != 0 { - return s.errstr(rc) +// int sqlite3_column_bytes(sqlite3_stmt*, int iCol); +func (c *conn) columnBytes(pstmt crt.Intptr, iCol int) (_ int, err error) { + v := bin.Xsqlite3_column_bytes(c.tls, pstmt, int32(iCol)) + return int(v), nil +} + +// const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol); +func (c *conn) columnText(pstmt crt.Intptr, iCol int) (v string, err error) { + p := bin.Xsqlite3_column_text(c.tls, pstmt, int32(iCol)) + len, err := c.columnBytes(pstmt, iCol) + if err != nil { + return "", err } - return nil + if p == 0 || len == 0 { + return "", nil + } + + b := make([]byte, len) + copy(b, (*crt.RawMem)(unsafe.Pointer(uintptr(p)))[:len]) + return string(b), nil } -// int sqlite3_bind_int(sqlite3_stmt*, int, int); -func (s *stmt) bindInt(pstmt crt.Intptr, idx1, value int) (err error) { - if rc := bin.Xsqlite3_bind_int(s.tls, pstmt, int32(idx1), int32(value)); rc != bin.DSQLITE_OK { - return s.errstr(rc) - } +// double sqlite3_column_double(sqlite3_stmt*, int iCol); +func (c *conn) columnDouble(pstmt crt.Intptr, iCol int) (v float64, err error) { + v = bin.Xsqlite3_column_double(c.tls, pstmt, int32(iCol)) + return v, nil +} - return nil +// sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol); +func (c *conn) columnInt64(pstmt crt.Intptr, iCol int) (v int64, err error) { + v = bin.Xsqlite3_column_int64(c.tls, pstmt, int32(iCol)) + return v, nil } -// int sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64); -func (s *stmt) bindInt64(pstmt crt.Intptr, idx1 int, value int64) (err error) { - if rc := bin.Xsqlite3_bind_int64(s.tls, pstmt, int32(idx1), value); rc != bin.DSQLITE_OK { - return s.errstr(rc) - } +// int sqlite3_column_type(sqlite3_stmt*, int iCol); +func (c *conn) columnType(pstmt crt.Intptr, iCol int) (_ int, err error) { + v := bin.Xsqlite3_column_type(c.tls, pstmt, int32(iCol)) + return int(v), nil +} - return nil +// const char *sqlite3_column_name(sqlite3_stmt*, int N); +func (c *conn) columnName(pstmt crt.Intptr, n int) (string, error) { + p := bin.Xsqlite3_column_name(c.tls, pstmt, int32(n)) + return crt.GoString(p), nil } -// int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*)); -func (s *stmt) bindBlob(pstmt crt.Intptr, idx1 int, value []byte) (err error) { - p, err := s.malloc(len(value)) - if err != nil { - return err - } +// int sqlite3_column_count(sqlite3_stmt *pStmt); +func (c *conn) columnCount(pstmt crt.Intptr) (_ int, err error) { + v := bin.Xsqlite3_column_count(c.tls, pstmt) + return int(v), nil +} - s.allocs = append(s.allocs, p) - copy((*crt.RawMem)(unsafe.Pointer(uintptr(p)))[:len(value)], value) - if rc := bin.Xsqlite3_bind_blob(s.tls, pstmt, int32(idx1), p, int32(len(value)), 0); rc != bin.DSQLITE_OK { - return s.errstr(rc) - } +// sqlite3_int64 sqlite3_last_insert_rowid(sqlite3*); +func (c *conn) lastInsertRowID() (v int64, _ error) { + return bin.Xsqlite3_last_insert_rowid(c.tls, c.db), nil +} - return nil +// int sqlite3_changes(sqlite3*); +func (c *conn) changes() (int, error) { + v := bin.Xsqlite3_changes(c.tls, c.db) + return int(v), nil } -// int sqlite3_bind_text(sqlite3_stmt*,int,const char*,int,void(*)(void*)); -func (s *stmt) bindText(pstmt crt.Intptr, idx1 int, value string) (err error) { - p, err := s.cString(value) - if err != nil { - return err +// int sqlite3_step(sqlite3_stmt*); +func (c *conn) step(pstmt crt.Intptr) (int, error) { + for { + switch rc := bin.Xsqlite3_step(c.tls, pstmt); rc { + case sqliteLockedSharedcache, bin.DSQLITE_BUSY: + if err := c.retry(pstmt); err != nil { + return bin.DSQLITE_LOCKED, err + } + default: + return int(rc), nil + } } +} - s.allocs = append(s.allocs, p) - if rc := bin.Xsqlite3_bind_text(s.tls, pstmt, int32(idx1), p, int32(len(value)), 0); rc != bin.DSQLITE_OK { - return s.errstr(rc) +func (c *conn) retry(pstmt crt.Intptr) error { + mu := mutexAlloc(c.tls, bin.DSQLITE_MUTEX_FAST) + (*mutex)(unsafe.Pointer(uintptr(mu))).enter(c.tls.ID) // Block + rc := bin.Xsqlite3_unlock_notify( + c.tls, + c.db, + *(*crt.Intptr)(unsafe.Pointer(&struct { + f func(*crt.TLS, crt.Intptr, int32) + }{unlockNotify})), + mu, + ) + if rc == bin.DSQLITE_LOCKED { // Deadlock, see https://www.sqlite.org/c3ref/unlock_notify.html + (*mutex)(unsafe.Pointer(uintptr(mu))).leave() // Clear + mutexFree(c.tls, mu) + return c.errstr(rc) } + (*mutex)(unsafe.Pointer(uintptr(mu))).enter(c.tls.ID) // Wait + (*mutex)(unsafe.Pointer(uintptr(mu))).leave() // Clear + mutexFree(c.tls, mu) + if pstmt != 0 { + bin.Xsqlite3_reset(c.tls, pstmt) + } return nil } -func (s *stmt) bind(pstmt crt.Intptr, n int, args []namedValue) error { +func unlockNotify(t *crt.TLS, ppArg crt.Intptr, nArg int32) { + for i := int32(0); i < nArg; i++ { + mu := *(*crt.Intptr)(unsafe.Pointer(uintptr(ppArg))) + (*mutex)(unsafe.Pointer(uintptr(mu))).leave() // Signal + ppArg += crt.Intptr(ptrSize) + } +} + +func (c *conn) bind(pstmt crt.Intptr, n int, args []driver.NamedValue) (allocs []crt.Intptr, err error) { + defer func() { + if err == nil { + return + } + + for _, v := range allocs { + c.free(v) + } + allocs = nil + }() + for i := 1; i <= n; i++ { - name, err := s.bindParameterName(pstmt, i) + var p crt.Intptr + name, err := c.bindParameterName(pstmt, i) if err != nil { - return err + return allocs, err } - var v namedValue + var v driver.NamedValue for _, v = range args { if name != "" { // sqlite supports '$', '@' and ':' prefixes for string @@ -597,75 +748,130 @@ func (s *stmt) bind(pstmt crt.Intptr, n int, args []namedValue) error { if v.Ordinal == 0 { if name != "" { - return fmt.Errorf("missing named argument %q", name[1:]) + return allocs, fmt.Errorf("missing named argument %q", name[1:]) } - return fmt.Errorf("missing argument with %d index", i) + return allocs, 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 + if err := c.bindInt64(pstmt, i, x); err != nil { + return allocs, err } case float64: - if err := s.bindDouble(pstmt, i, x); err != nil { - return err + if err := c.bindDouble(pstmt, i, x); err != nil { + return allocs, err } case bool: v := 0 if x { v = 1 } - if err := s.bindInt(pstmt, i, v); err != nil { - return err + if err := c.bindInt(pstmt, i, v); err != nil { + return allocs, err } case []byte: - if err := s.bindBlob(pstmt, i, x); err != nil { - return err + if p, err = c.bindBlob(pstmt, i, x); err != nil { + return allocs, err } case string: - if err := s.bindText(pstmt, i, x); err != nil { - return err + if p, err = c.bindText(pstmt, i, x); err != nil { + return allocs, err } case time.Time: - if err := s.bindText(pstmt, i, x.String()); err != nil { - return err + if p, err = c.bindText(pstmt, i, x.String()); err != nil { + return allocs, err } default: - return fmt.Errorf("invalid driver.Value type %T", x) + return allocs, fmt.Errorf("sqlite: invalid driver.Value type %T", x) + } + if p != 0 { + allocs = append(allocs, p) } } + return allocs, nil +} + +// int sqlite3_bind_text(sqlite3_stmt*,int,const char*,int,void(*)(void*)); +func (c *conn) bindText(pstmt crt.Intptr, idx1 int, value string) (crt.Intptr, error) { + p, err := crt.CString(value) + if err != nil { + return 0, err + } + + if rc := bin.Xsqlite3_bind_text(c.tls, pstmt, int32(idx1), p, int32(len(value)), 0); rc != bin.DSQLITE_OK { + c.free(p) + return 0, c.errstr(rc) + } + + return p, nil +} + +// int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*)); +func (c *conn) bindBlob(pstmt crt.Intptr, idx1 int, value []byte) (crt.Intptr, error) { + p, err := c.malloc(len(value)) + if err != nil { + return 0, err + } + + copy((*crt.RawMem)(unsafe.Pointer(uintptr(p)))[:len(value)], value) + if rc := bin.Xsqlite3_bind_blob(c.tls, pstmt, int32(idx1), p, int32(len(value)), 0); rc != bin.DSQLITE_OK { + c.free(p) + return 0, c.errstr(rc) + } + + return p, nil +} + +// int sqlite3_bind_int(sqlite3_stmt*, int, int); +func (c *conn) bindInt(pstmt crt.Intptr, idx1, value int) (err error) { + if rc := bin.Xsqlite3_bind_int(c.tls, pstmt, int32(idx1), int32(value)); rc != bin.DSQLITE_OK { + return c.errstr(rc) + } + return nil } -// int sqlite3_bind_parameter_count(sqlite3_stmt*); -func (s *stmt) bindParameterCount(pstmt crt.Intptr) (_ int, err error) { - r := bin.Xsqlite3_bind_parameter_count(s.tls, pstmt) - return int(r), nil +// int sqlite3_bind_double(sqlite3_stmt*, int, double); +func (c *conn) bindDouble(pstmt crt.Intptr, idx1 int, value float64) (err error) { + if rc := bin.Xsqlite3_bind_double(c.tls, pstmt, int32(idx1), value); rc != 0 { + return c.errstr(rc) + } + + return nil +} + +// int sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64); +func (c *conn) bindInt64(pstmt crt.Intptr, idx1 int, value int64) (err error) { + if rc := bin.Xsqlite3_bind_int64(c.tls, pstmt, int32(idx1), value); rc != bin.DSQLITE_OK { + return c.errstr(rc) + } + + return nil } // const char *sqlite3_bind_parameter_name(sqlite3_stmt*, int); -func (s *stmt) bindParameterName(pstmt crt.Intptr, i int) (string, error) { - p := bin.Xsqlite3_bind_parameter_name(s.tls, pstmt, int32(i)) +func (c *conn) bindParameterName(pstmt crt.Intptr, i int) (string, error) { + p := bin.Xsqlite3_bind_parameter_name(c.tls, pstmt, int32(i)) return crt.GoString(p), nil } +// int sqlite3_bind_parameter_count(sqlite3_stmt*); +func (c *conn) bindParameterCount(pstmt crt.Intptr) (_ int, err error) { + r := bin.Xsqlite3_bind_parameter_count(c.tls, pstmt) + return int(r), nil +} + // int sqlite3_finalize(sqlite3_stmt *pStmt); -func (s *stmt) finalize(pstmt crt.Intptr) error { - if rc := bin.Xsqlite3_finalize(s.tls, pstmt); rc != bin.DSQLITE_OK { - return s.errstr(rc) +func (c *conn) finalize(pstmt crt.Intptr) error { + if rc := bin.Xsqlite3_finalize(c.tls, pstmt); rc != bin.DSQLITE_OK { + return c.errstr(rc) } return nil } -// int sqlite3_step(sqlite3_stmt*); -func (s *stmt) step(pstmt crt.Intptr) (int, error) { - r := bin.Xsqlite3_step(s.tls, pstmt) - return int(r), nil -} - // int sqlite3_prepare_v2( // sqlite3 *db, /* Database handle */ // const char *zSql, /* SQL statement, UTF-8 encoded */ @@ -673,109 +879,122 @@ func (s *stmt) step(pstmt crt.Intptr) (int, error) { // sqlite3_stmt **ppStmt, /* OUT: Statement handle */ // const char **pzTail /* OUT: Pointer to unused portion of zSql */ // ); -func (s *stmt) prepareV2(zSQL crt.Intptr) error { - if rc := bin.Xsqlite3_prepare_v2(s.tls, s.pdb(), zSQL, -1, s.ppstmt, s.pzTail); rc != bin.DSQLITE_OK { - return s.errstr(rc) - } +func (c *conn) prepareV2(zSql *crt.Intptr) (pstmt crt.Intptr, err error) { + var ppstmt, pptail crt.Intptr - return nil -} + defer func() { + c.free(ppstmt) + c.free(pptail) + }() -type tx struct { - *conn -} + if ppstmt, err = c.malloc(ptrSize); err != nil { + return 0, err + } -func newTx(c *conn) (*tx, error) { - t := &tx{conn: c} - if err := t.exec(context.Background(), "begin"); err != nil { - return nil, err + if pptail, err = c.malloc(ptrSize); err != nil { + return 0, err } - return t, nil + for { + switch rc := bin.Xsqlite3_prepare_v2(c.tls, c.db, *zSql, -1, ppstmt, pptail); rc { + case bin.DSQLITE_OK: + *zSql = *(*crt.Intptr)(unsafe.Pointer(uintptr(pptail))) + return *(*crt.Intptr)(unsafe.Pointer(uintptr(ppstmt))), nil + case sqliteLockedSharedcache, bin.DSQLITE_BUSY: + if err := c.retry(0); err != nil { + return 0, err + } + default: + return 0, c.errstr(rc) + } + } } -// Commit implements driver.Tx. -func (t *tx) Commit() (err error) { - return t.exec(context.Background(), "commit") +// void sqlite3_interrupt(sqlite3*); +func (c *conn) interrupt(pdb crt.Intptr) (err error) { + bin.Xsqlite3_interrupt(c.tls, pdb) + return nil } -// Rollback implements driver.Tx. -func (t *tx) Rollback() (err error) { - return t.exec(context.Background(), "rollback") +// int sqlite3_extended_result_codes(sqlite3*, int onoff); +func (c *conn) extendedResultCodes(on bool) error { + if rc := bin.Xsqlite3_extended_result_codes(c.tls, c.db, crt.Bool32(on)); rc != bin.DSQLITE_OK { + return c.errstr(rc) + } + + return nil } -// 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 */ +// 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 (t *tx) exec(ctx context.Context, sql string) (err error) { - psql, err := t.cString(sql) - if err != nil { - return err - } - - defer t.free(psql) +func (c *conn) openV2(name string, flags int32) (crt.Intptr, error) { + var p, s crt.Intptr - //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: + defer func() { + if p != 0 { + c.free(p) + } + if s != 0 { + c.free(s) } }() - if rc := bin.Xsqlite3_exec(t.tls, t.pdb(), psql, 0, 0, 0); rc != bin.DSQLITE_OK { - return t.errstr(rc) + p, err := c.malloc(ptrSize) + if err != nil { + return 0, err } - return nil -} + if s, err = crt.CString(name); err != nil { + return 0, err + } -type conn struct { - *Driver - ppdb crt.Intptr // **bin.Xsqlite3 - tls *crt.TLS + if rc := bin.Xsqlite3_open_v2(c.tls, s, p, flags, 0); rc != bin.DSQLITE_OK { + return 0, c.errstr(rc) + } + + return *(*crt.Intptr)(unsafe.Pointer(uintptr(p))), nil } -func newConn(s *Driver, name string) (_ *conn, err error) { - c := &conn{Driver: s} +func (c *conn) malloc(n int) (crt.Intptr, error) { + if p := crt.Xmalloc(c.tls, crt.Intptr(n)); p != 0 { + return p, nil + } - defer func() { - if err != nil { - c.close() - } - }() + return 0, fmt.Errorf("sqlite: cannot allocate %d bytes of memory", n) +} - c.tls = crt.NewTLS() - if err = c.openV2( - name, - bin.DSQLITE_OPEN_READWRITE|bin.DSQLITE_OPEN_CREATE| - bin.DSQLITE_OPEN_FULLMUTEX| - bin.DSQLITE_OPEN_URI, - ); err != nil { - return nil, err +func (c *conn) free(p crt.Intptr) { + if p != 0 { + crt.Xfree(c.tls, p) } +} - if err = c.extendedResultCodes(true); err != nil { - return nil, err +// const char *sqlite3_errstr(int); +func (c *conn) errstr(rc int32) error { + p := bin.Xsqlite3_errstr(c.tls, rc) + str := crt.GoString(p) + p = bin.Xsqlite3_errmsg(c.tls, c.db) + switch msg := crt.GoString(p); { + case msg == str: + return &Error{msg: fmt.Sprintf("%s (%v)", str, rc), code: int(rc)} + default: + return &Error{msg: fmt.Sprintf("%s: %s (%v)", str, msg, rc), code: int(rc)} } - - 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) +// Begin starts a transaction. +// +// Deprecated: Drivers should implement ConnBeginTx instead (or additionally). +func (c *conn) Begin() (driver.Tx, error) { + return c.begin(context.Background(), driver.TxOptions{}) } -func (c *conn) prepare(ctx context.Context, query string) (s driver.Stmt, err error) { - return newStmt(c, query) +func (c *conn) begin(ctx context.Context, opts driver.TxOptions) (t driver.Tx, err error) { + return newTx(c) } // Close invalidates and potentially stops any current prepared statements and @@ -784,23 +1003,24 @@ func (c *conn) prepare(ctx context.Context, query string) (s driver.Stmt, err er // 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) { - return c.close() -} +func (c *conn) Close() error { + if c.db != 0 { + if err := c.closeV2(c.db); err != nil { + return err + } -// Begin starts a transaction. -func (c *conn) Begin() (driver.Tx, error) { - return c.begin(context.Background(), txOptions{}) + c.db = 0 + } + return nil } -// copy of driver.TxOptions -type txOptions struct { - Isolation int // driver.IsolationLevel - ReadOnly bool -} +// int sqlite3_close_v2(sqlite3*); +func (c *conn) closeV2(db crt.Intptr) error { + if rc := bin.Xsqlite3_close_v2(c.tls, db); rc != bin.DSQLITE_OK { + return c.errstr(rc) + } -func (c *conn) begin(ctx context.Context, opts txOptions) (t driver.Tx, err error) { - return newTx(c) + return nil } // Execer is an optional interface that may be implemented by a Conn. @@ -809,11 +1029,13 @@ func (c *conn) begin(ctx context.Context, opts txOptions) (t driver.Tx, err erro // prepare a query, execute the statement, and then close the statement. // // Exec may return ErrSkip. +// +// Deprecated: Drivers should implement ExecerContext instead. 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) { +func (c *conn) exec(ctx context.Context, query string, args []driver.NamedValue) (r driver.Result, err error) { s, err := c.prepare(ctx, query) if err != nil { return nil, err @@ -828,20 +1050,14 @@ func (c *conn) exec(ctx context.Context, query string, args []namedValue) (r dri return s.(*stmt).exec(ctx, args) } -// copy of driver.NameValue -type namedValue struct { - Name string - Ordinal int - Value driver.Value +// Prepare returns a prepared statement, bound to this connection. +func (c *conn) Prepare(query string) (driver.Stmt, error) { + return c.prepare(context.Background(), query) } -// 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 +func (c *conn) prepare(ctx context.Context, query string) (s driver.Stmt, err error) { + //TODO use ctx + return newStmt(c, query) } // Queryer is an optional interface that may be implemented by a Conn. @@ -850,11 +1066,13 @@ func toNamedValues(vals []driver.Value) []namedValue { // prepare a query, execute the statement, and then close the statement. // // Query may return ErrSkip. +// +// Deprecated: Drivers should implement QueryerContext instead. 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) { +func (c *conn) query(ctx context.Context, query string, args []driver.NamedValue) (r driver.Rows, err error) { s, err := c.prepare(ctx, query) if err != nil { return nil, err @@ -869,124 +1087,10 @@ func (c *conn) query(ctx context.Context, query string, args []namedValue) (r dr return s.(*stmt).query(ctx, args) } -func (c *conn) pdb() crt.Intptr { return *(*crt.Intptr)(unsafe.Pointer(uintptr(c.ppdb))) } - -// int sqlite3_extended_result_codes(sqlite3*, int onoff); -func (c *conn) extendedResultCodes(on bool) (err error) { - if rc := bin.Xsqlite3_extended_result_codes(c.tls, c.pdb(), crt.Bool32(on)); rc != bin.DSQLITE_OK { - return c.errstr(rc) - } - - return nil -} - -// void *sqlite3_malloc(int); -func (c *conn) malloc(n int) (r crt.Intptr, err error) { - if n > math.MaxInt32 { - return 0, fmt.Errorf("cannot allocate %d bytes of memory", n) - } - - r = bin.Xsqlite3_malloc(c.tls, int32(n)) - if r == 0 { - return 0, fmt.Errorf("malloc(%v) failed", n) - } - - return r, nil -} - -func (c *conn) cString(s string) (crt.Intptr, error) { - n := len(s) - p, err := c.malloc(n + 1) - if err != nil { - return 0, err - } - - copy((*crt.RawMem)(unsafe.Pointer(uintptr(p)))[:n], s) - *(*byte)(unsafe.Pointer(uintptr(p) + uintptr(n))) = 0 - 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 - if rc := bin.Xsqlite3_open_v2(c.tls, filename, c.ppdb, flags, 0); rc != bin.DSQLITE_OK { - return c.errstr(rc) - } - - return nil -} - -// const char *sqlite3_errstr(int); -func (c *conn) errstr(rc int32) (err error) { - p := bin.Xsqlite3_errstr(c.tls, rc) - str := crt.GoString(p) - p = bin.Xsqlite3_errmsg(c.tls, c.pdb()) - - switch msg := crt.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) { - if rc := bin.Xsqlite3_close_v2(c.tls, c.pdb()); rc != bin.DSQLITE_OK { - return c.errstr(rc) - } - - err = c.free(c.ppdb) - c.ppdb = 0 - return err -} - -// void sqlite3_free(void*); -func (c *conn) free(p crt.Intptr) (err error) { - bin.Xsqlite3_free(c.tls, p) - return nil -} - -// void sqlite3_interrupt(sqlite3*); -func (c *conn) interrupt(pdb crt.Intptr) (err error) { - bin.Xsqlite3_interrupt(c.tls, pdb) - return nil -} - -func (c *conn) close() (err error) { - c.Lock() - - defer c.Unlock() - - if c.ppdb != 0 { - err = c.closeV2() - } - return err -} - // Driver implements database/sql/driver.Driver. -type Driver struct { - sync.Mutex -} +type Driver struct{} -func newDrv() *Driver { return &Driver{} } +func newDriver() *Driver { return &Driver{} } // Open returns a new connection to the database. The name is a string in a // driver-specific format. @@ -996,6 +1100,6 @@ func newDrv() *Driver { return &Driver{} } // 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) { - return newConn(s, name) +func (d *Driver) Open(name string) (driver.Conn, error) { + return newConn(name) } |