diff options
author | EuAndreh <eu@euandre.org> | 2024-10-01 07:47:29 -0300 |
---|---|---|
committer | EuAndreh <eu@euandre.org> | 2024-10-01 07:49:17 -0300 |
commit | df51f67c058f4b0832236c91abfdc8f9485e6ff9 (patch) | |
tree | 72f79684a0c8609d7a5e84b14e40c18902b03fd0 /src/golite.go | |
parent | src/golite.go: Update doccomments on Open() (diff) | |
download | golite-df51f67c058f4b0832236c91abfdc8f9485e6ff9.tar.gz golite-df51f67c058f4b0832236c91abfdc8f9485e6ff9.tar.xz |
Remove code for modules, vtables, tracing and ad-hoc C functions
Diffstat (limited to 'src/golite.go')
-rw-r--r-- | src/golite.go | 1195 |
1 files changed, 100 insertions, 1095 deletions
diff --git a/src/golite.go b/src/golite.go index 8d9218c..d3e2f19 100644 --- a/src/golite.go +++ b/src/golite.go @@ -30,358 +30,17 @@ Premises: */ /* - -#include <stdint.h> -#include <stdio.h> #include <stdlib.h> -#include <string.h> - #include <sqlite3.h> -// You can't export a Go function to C and have definitions in the C -// preamble in the same file, so we have to have callbackTrampoline in -// its own file. Because we need a separate file anyway, the support -// code for SQLite custom functions is in here. - -static int -_sqlite3_open_v2(const char *filename, sqlite3 **ppDb, int flags, const char *zVfs) { - // FIXME: remove obligatory flag - return sqlite3_open_v2(filename, ppDb, flags | SQLITE_OPEN_URI, zVfs); -} - -static int -_sqlite3_bind_text(sqlite3_stmt *stmt, int n, char *p, int np) { - return sqlite3_bind_text(stmt, n, p, np, SQLITE_TRANSIENT); -} - -static int -_sqlite3_bind_blob(sqlite3_stmt *stmt, int n, void *p, int np) { - return sqlite3_bind_blob(stmt, n, p, np, SQLITE_TRANSIENT); -} - -static int -_sqlite3_exec(sqlite3* db, const char* pcmd, long long* rowid, long long* changes) -{ - int rv = sqlite3_exec(db, pcmd, 0, 0, 0); - *rowid = (long long) sqlite3_last_insert_rowid(db); - *changes = (long long) sqlite3_changes(db); - return rv; -} - -static int -_sqlite3_step_internal(sqlite3_stmt *stmt) -{ - return sqlite3_step(stmt); -} - -static int -_sqlite3_step_row_internal(sqlite3_stmt* stmt, long long* rowid, long long* changes) -{ - int rv = sqlite3_step(stmt); - sqlite3* db = sqlite3_db_handle(stmt); - *rowid = (long long) sqlite3_last_insert_rowid(db); - *changes = (long long) sqlite3_changes(db); - return rv; -} - -static int -_sqlite3_prepare_v2_internal(sqlite3 *db, const char *zSql, int nBytes, sqlite3_stmt **ppStmt, const char **pzTail) -{ - return sqlite3_prepare_v2(db, zSql, nBytes, ppStmt, pzTail); -} - -static -void _sqlite3_result_text(sqlite3_context* ctx, const char* s) { - sqlite3_result_text(ctx, s, -1, &free); -} - -static -void _sqlite3_result_blob(sqlite3_context* ctx, const void* b, int l) { - sqlite3_result_blob(ctx, b, l, SQLITE_TRANSIENT); -} - -static -int _sqlite3_create_function( - sqlite3 *db, - const char *zFunctionName, - int nArg, - int eTextRep, - uintptr_t pApp, - void (*xFunc)(sqlite3_context*,int,sqlite3_value**), - void (*xStep)(sqlite3_context*,int,sqlite3_value**), - void (*xFinal)(sqlite3_context*) -) { - return sqlite3_create_function(db, zFunctionName, nArg, eTextRep, (void*) pApp, xFunc, xStep, xFinal); -} - void callbackTrampoline(sqlite3_context*, int, sqlite3_value**); -void stepTrampoline(sqlite3_context*, int, sqlite3_value**); -void doneTrampoline(sqlite3_context*); +void stepTrampoline(sqlite3_context*, int, sqlite3_value**); +void doneTrampoline(sqlite3_context*); -int compareTrampoline(void*, int, char*, int, char*); -int commitHookTrampoline(void*); +int compareTrampoline(void*, int, char*, int, char*); +int commitHookTrampoline(void*); void rollbackHookTrampoline(void*); -void updateHookTrampoline(void*, int, char*, char*, sqlite3_int64); - -int authorizerTrampoline(void*, int, char*, char*, char*, char*); - -static int _sqlite3_limit(sqlite3* db, int limitId, int newLimit) { - return sqlite3_limit(db, limitId, newLimit); -} - -// These wrappers are necessary because SQLITE_TRANSIENT -// is a pointer constant, and cgo doesn't translate them correctly. - -static inline void my_result_text(sqlite3_context *ctx, char *p, int np) { - sqlite3_result_text(ctx, p, np, SQLITE_TRANSIENT); -} - -static inline void my_result_blob(sqlite3_context *ctx, void *p, int np) { - sqlite3_result_blob(ctx, p, np, SQLITE_TRANSIENT); -} - -extern void unlock_notify_callback(void *arg, int argc); - -static inline char *_sqlite3_mprintf(char *zFormat, char *arg) { - return sqlite3_mprintf(zFormat, arg); -} - -typedef struct goVTab goVTab; - -struct goVTab { - sqlite3_vtab base; - void *vTab; -}; - -uintptr_t goMInit(void *db, void *pAux, int argc, char **argv, char **pzErr, int isCreate); - -static int cXInit(sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVTab, char **pzErr, int isCreate) { - void *vTab = (void *)goMInit(db, pAux, argc, (char**)argv, pzErr, isCreate); - if (!vTab || *pzErr) { - return SQLITE_ERROR; - } - goVTab *pvTab = (goVTab *)sqlite3_malloc(sizeof(goVTab)); - if (!pvTab) { - *pzErr = sqlite3_mprintf("%s", "Out of memory"); - return SQLITE_NOMEM; - } - memset(pvTab, 0, sizeof(goVTab)); - pvTab->vTab = vTab; - - *ppVTab = (sqlite3_vtab *)pvTab; - *pzErr = 0; - return SQLITE_OK; -} - -static inline int cXCreate(sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVTab, char **pzErr) { - return cXInit(db, pAux, argc, argv, ppVTab, pzErr, 1); -} -static inline int cXConnect(sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVTab, char **pzErr) { - return cXInit(db, pAux, argc, argv, ppVTab, pzErr, 0); -} - -char* goVBestIndex(void *pVTab, void *icp); - -static inline int cXBestIndex(sqlite3_vtab *pVTab, sqlite3_index_info *info) { - char *pzErr = goVBestIndex(((goVTab*)pVTab)->vTab, info); - if (pzErr) { - if (pVTab->zErrMsg) - sqlite3_free(pVTab->zErrMsg); - pVTab->zErrMsg = pzErr; - return SQLITE_ERROR; - } - return SQLITE_OK; -} - -char* goVRelease(void *pVTab, int isDestroy); - -static int cXRelease(sqlite3_vtab *pVTab, int isDestroy) { - char *pzErr = goVRelease(((goVTab*)pVTab)->vTab, isDestroy); - if (pzErr) { - if (pVTab->zErrMsg) - sqlite3_free(pVTab->zErrMsg); - pVTab->zErrMsg = pzErr; - return SQLITE_ERROR; - } - if (pVTab->zErrMsg) - sqlite3_free(pVTab->zErrMsg); - sqlite3_free(pVTab); - return SQLITE_OK; -} - -static inline int cXDisconnect(sqlite3_vtab *pVTab) { - return cXRelease(pVTab, 0); -} - -static inline int cXDestroy(sqlite3_vtab *pVTab) { - return cXRelease(pVTab, 1); -} - -typedef struct goVTabCursor goVTabCursor; - -struct goVTabCursor { - sqlite3_vtab_cursor base; - void *vTabCursor; -}; - -uintptr_t goVOpen(void *pVTab, char **pzErr); - -static int cXOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor) { - void *vTabCursor = (void *)goVOpen(((goVTab*)pVTab)->vTab, &(pVTab->zErrMsg)); - goVTabCursor *pCursor = (goVTabCursor *)sqlite3_malloc(sizeof(goVTabCursor)); - if (!pCursor) { - return SQLITE_NOMEM; - } - memset(pCursor, 0, sizeof(goVTabCursor)); - pCursor->vTabCursor = vTabCursor; - *ppCursor = (sqlite3_vtab_cursor *)pCursor; - return SQLITE_OK; -} - -static int setErrMsg(sqlite3_vtab_cursor *pCursor, char *pzErr) { - if (pCursor->pVtab->zErrMsg) - sqlite3_free(pCursor->pVtab->zErrMsg); - pCursor->pVtab->zErrMsg = pzErr; - return SQLITE_ERROR; -} - -char* goVClose(void *pCursor); - -static int cXClose(sqlite3_vtab_cursor *pCursor) { - char *pzErr = goVClose(((goVTabCursor*)pCursor)->vTabCursor); - if (pzErr) { - return setErrMsg(pCursor, pzErr); - } - - sqlite3_free(pCursor); - return SQLITE_OK; -} - -char* goVFilter(void *pCursor, int idxNum, char* idxName, int argc, sqlite3_value **argv); - -static int cXFilter(sqlite3_vtab_cursor *pCursor, int idxNum, const char *idxStr, int argc, sqlite3_value **argv) { - char *pzErr = goVFilter(((goVTabCursor*)pCursor)->vTabCursor, idxNum, (char*)idxStr, argc, argv); - if (pzErr) { - return setErrMsg(pCursor, pzErr); - } - return SQLITE_OK; -} - -char* goVNext(void *pCursor); - -static int cXNext(sqlite3_vtab_cursor *pCursor) { - char *pzErr = goVNext(((goVTabCursor*)pCursor)->vTabCursor); - if (pzErr) { - return setErrMsg(pCursor, pzErr); - } - return SQLITE_OK; -} - -int goVEof(void *pCursor); - -static inline int cXEof(sqlite3_vtab_cursor *pCursor) { - return goVEof(((goVTabCursor*)pCursor)->vTabCursor); -} - -char* goVColumn(void *pCursor, void *cp, int col); - -static int cXColumn(sqlite3_vtab_cursor *pCursor, sqlite3_context *ctx, int i) { - char *pzErr = goVColumn(((goVTabCursor*)pCursor)->vTabCursor, ctx, i); - if (pzErr) { - return setErrMsg(pCursor, pzErr); - } - return SQLITE_OK; -} - -char* goVRowid(void *pCursor, sqlite3_int64 *pRowid); - -static int cXRowid(sqlite3_vtab_cursor *pCursor, sqlite3_int64 *pRowid) { - char *pzErr = goVRowid(((goVTabCursor*)pCursor)->vTabCursor, pRowid); - if (pzErr) { - return setErrMsg(pCursor, pzErr); - } - return SQLITE_OK; -} - -char* goVUpdate(void *pVTab, int argc, sqlite3_value **argv, sqlite3_int64 *pRowid); - -static int cXUpdate(sqlite3_vtab *pVTab, int argc, sqlite3_value **argv, sqlite3_int64 *pRowid) { - char *pzErr = goVUpdate(((goVTab*)pVTab)->vTab, argc, argv, pRowid); - if (pzErr) { - if (pVTab->zErrMsg) - sqlite3_free(pVTab->zErrMsg); - pVTab->zErrMsg = pzErr; - return SQLITE_ERROR; - } - return SQLITE_OK; -} - -static sqlite3_module goModule = { - 0, // iVersion - cXCreate, // xCreate - create a table - cXConnect, // xConnect - connect to an existing table - cXBestIndex, // xBestIndex - Determine search strategy - cXDisconnect, // xDisconnect - Disconnect from a table - cXDestroy, // xDestroy - Drop a table - cXOpen, // xOpen - open a cursor - cXClose, // xClose - close a cursor - cXFilter, // xFilter - configure scan constraints - cXNext, // xNext - advance a cursor - cXEof, // xEof - cXColumn, // xColumn - read data - cXRowid, // xRowid - read data - cXUpdate, // xUpdate - write data -// Not implemented - 0, // xBegin - begin transaction - 0, // xSync - sync transaction - 0, // xCommit - commit transaction - 0, // xRollback - rollback transaction - 0, // xFindFunction - function overloading - 0, // xRename - rename the table - 0, // xSavepoint - 0, // xRelease - 0, // xRollbackTo -}; - -// See https://sqlite.org/vtab.html#eponymous_only_virtual_tables -static sqlite3_module goModuleEponymousOnly = { - 0, // iVersion - 0, // xCreate - create a table, which here is null - cXConnect, // xConnect - connect to an existing table - cXBestIndex, // xBestIndex - Determine search strategy - cXDisconnect, // xDisconnect - Disconnect from a table - cXDestroy, // xDestroy - Drop a table - cXOpen, // xOpen - open a cursor - cXClose, // xClose - close a cursor - cXFilter, // xFilter - configure scan constraints - cXNext, // xNext - advance a cursor - cXEof, // xEof - cXColumn, // xColumn - read data - cXRowid, // xRowid - read data - cXUpdate, // xUpdate - write data -// Not implemented - 0, // xBegin - begin transaction - 0, // xSync - sync transaction - 0, // xCommit - commit transaction - 0, // xRollback - rollback transaction - 0, // xFindFunction - function overloading - 0, // xRename - rename the table - 0, // xSavepoint - 0, // xRelease - 0 // xRollbackTo -}; - -void goMDestroy(void*); - -static int _sqlite3_create_module(sqlite3 *db, const char *zName, uintptr_t pClientData) { - return sqlite3_create_module_v2(db, zName, &goModule, (void*) pClientData, goMDestroy); -} - -static int _sqlite3_create_module_eponymous_only(sqlite3 *db, const char *zName, uintptr_t pClientData) { - return sqlite3_create_module_v2(db, zName, &goModuleEponymousOnly, (void*) pClientData, goMDestroy); -} - -int traceCallbackTrampoline(unsigned int traceEventCode, void *ctx, void *p, void *x); +void updateHookTrampoline(void*, int, char*, char*, sqlite3_int64); */ import "C" @@ -522,9 +181,6 @@ func deleteHandles(db *SQLiteConn) { } } -// This is only here so that tests can refer to it. -type callbackArgRaw C.sqlite3_value - type callbackArgConverter func(*C.sqlite3_value) (reflect.Value, error) type callbackArgCast struct { @@ -719,7 +375,12 @@ func callbackRetBlob(ctx *C.sqlite3_context, v reflect.Value) error { C.sqlite3_result_null(ctx) } else { bs := i.([]byte) - C._sqlite3_result_blob(ctx, unsafe.Pointer(&bs[0]), C.int(len(bs))) + C.sqlite3_result_blob( + ctx, + unsafe.Pointer(&bs[0]), + C.int(len(bs)), + C.SQLITE_TRANSIENT, + ) } return nil } @@ -728,7 +389,12 @@ func callbackRetText(ctx *C.sqlite3_context, v reflect.Value) error { if v.Type().Kind() != reflect.String { return fmt.Errorf("cannot convert %s to TEXT", v.Type()) } - C._sqlite3_result_text(ctx, C.CString(v.Interface().(string))) + C.sqlite3_result_text( + ctx, + C.CString(v.Interface().(string)), + -1, + (*[0]byte)(unsafe.Pointer(C.free)), + ) return nil } @@ -785,6 +451,7 @@ func callbackError(ctx *C.sqlite3_context, err error) { C.sqlite3_result_error(ctx, cstr, C.int(-1)) } +// FIXME: remove this // Test support code. Tests are not allowed to import "C", so we can't // declare any functions that use C.sqlite3_value. func callbackSyntheticForTests(v reflect.Value, err error) callbackArgConverter { @@ -1341,6 +1008,7 @@ var ( ErrWarningAutoIndex = ErrWarning.Extend(1) ) +// FIXME: remove this // SQLiteTimestampFormats is timestamp formats understood by both this module // and SQLite. The first format in the slice will be used when saving time // values into the database. When parsing a string from a timestamp or datetime @@ -1379,48 +1047,10 @@ func LibVersion() (libVersion string, libVersionNumber int, sourceID string) { } const ( - // used by authorizer and pre_update_hook + // used by update hook SQLITE_DELETE = C.SQLITE_DELETE SQLITE_INSERT = C.SQLITE_INSERT SQLITE_UPDATE = C.SQLITE_UPDATE - - // used by authorzier - as return value - SQLITE_OK = C.SQLITE_OK - SQLITE_IGNORE = C.SQLITE_IGNORE - SQLITE_DENY = C.SQLITE_DENY - - // different actions query tries to do - passed as argument to authorizer - SQLITE_CREATE_INDEX = C.SQLITE_CREATE_INDEX - SQLITE_CREATE_TABLE = C.SQLITE_CREATE_TABLE - SQLITE_CREATE_TEMP_INDEX = C.SQLITE_CREATE_TEMP_INDEX - SQLITE_CREATE_TEMP_TABLE = C.SQLITE_CREATE_TEMP_TABLE - SQLITE_CREATE_TEMP_TRIGGER = C.SQLITE_CREATE_TEMP_TRIGGER - SQLITE_CREATE_TEMP_VIEW = C.SQLITE_CREATE_TEMP_VIEW - SQLITE_CREATE_TRIGGER = C.SQLITE_CREATE_TRIGGER - SQLITE_CREATE_VIEW = C.SQLITE_CREATE_VIEW - SQLITE_CREATE_VTABLE = C.SQLITE_CREATE_VTABLE - SQLITE_DROP_INDEX = C.SQLITE_DROP_INDEX - SQLITE_DROP_TABLE = C.SQLITE_DROP_TABLE - SQLITE_DROP_TEMP_INDEX = C.SQLITE_DROP_TEMP_INDEX - SQLITE_DROP_TEMP_TABLE = C.SQLITE_DROP_TEMP_TABLE - SQLITE_DROP_TEMP_TRIGGER = C.SQLITE_DROP_TEMP_TRIGGER - SQLITE_DROP_TEMP_VIEW = C.SQLITE_DROP_TEMP_VIEW - SQLITE_DROP_TRIGGER = C.SQLITE_DROP_TRIGGER - SQLITE_DROP_VIEW = C.SQLITE_DROP_VIEW - SQLITE_DROP_VTABLE = C.SQLITE_DROP_VTABLE - SQLITE_PRAGMA = C.SQLITE_PRAGMA - SQLITE_READ = C.SQLITE_READ - SQLITE_SELECT = C.SQLITE_SELECT - SQLITE_TRANSACTION = C.SQLITE_TRANSACTION - SQLITE_ATTACH = C.SQLITE_ATTACH - SQLITE_DETACH = C.SQLITE_DETACH - SQLITE_ALTER_TABLE = C.SQLITE_ALTER_TABLE - SQLITE_REINDEX = C.SQLITE_REINDEX - SQLITE_ANALYZE = C.SQLITE_ANALYZE - SQLITE_FUNCTION = C.SQLITE_FUNCTION - SQLITE_SAVEPOINT = C.SQLITE_SAVEPOINT - SQLITE_COPY = C.SQLITE_COPY - /*SQLITE_RECURSIVE = C.SQLITE_RECURSIVE*/ ) // Standard File Control Opcodes @@ -1660,13 +1290,6 @@ func (c *SQLiteConn) RegisterCollation(name string, cmp func(string, string) int return nil } -// RegisterCommitHook sets the commit hook for a connection. -// -// If the callback returns non-zero the transaction will become a rollback. -// -// If there is an existing commit hook for this connection, it will be -// removed. If callback is nil the existing hook (if any) will be removed -// without creating a new one. func (c *SQLiteConn) RegisterCommitHook(callback func() int) { if callback == nil { C.sqlite3_commit_hook(c.db, nil, nil) @@ -1675,11 +1298,6 @@ func (c *SQLiteConn) RegisterCommitHook(callback func() int) { } } -// RegisterRollbackHook sets the rollback hook for a connection. -// -// If there is an existing rollback hook for this connection, it will be -// removed. If callback is nil the existing hook (if any) will be removed -// without creating a new one. func (c *SQLiteConn) RegisterRollbackHook(callback func()) { if callback == nil { C.sqlite3_rollback_hook(c.db, nil, nil) @@ -1688,15 +1306,6 @@ func (c *SQLiteConn) RegisterRollbackHook(callback func()) { } } -// RegisterUpdateHook sets the update hook for a connection. -// -// The parameters to the callback are the operation (one of the constants -// SQLITE_INSERT, SQLITE_DELETE, or SQLITE_UPDATE), the database name, the -// table name, and the rowid. -// -// If there is an existing update hook for this connection, it will be -// removed. If callback is nil the existing hook (if any) will be removed -// without creating a new one. func (c *SQLiteConn) RegisterUpdateHook(callback func(int, string, string, int64)) { if callback == nil { C.sqlite3_update_hook(c.db, nil, nil) @@ -1705,20 +1314,6 @@ func (c *SQLiteConn) RegisterUpdateHook(callback func(int, string, string, int64 } } -// RegisterAuthorizer sets the authorizer for connection. -// -// The parameters to the callback are the operation (one of the constants -// SQLITE_INSERT, SQLITE_DELETE, or SQLITE_UPDATE), and 1 to 3 arguments, -// depending on operation. More details see: -// https://www.sqlite.org/c3ref/c_alter_table.html -func (c *SQLiteConn) RegisterAuthorizer(callback func(int, string, string, string) int) { - if callback == nil { - C.sqlite3_set_authorizer(c.db, nil, nil) - } else { - C.sqlite3_set_authorizer(c.db, (*[0]byte)(C.authorizerTrampoline), newHandle(c, callback)) - } -} - // RegisterFunc makes a Go function available as a SQLite function. // // The Go function can have arguments of the following types: any @@ -1788,17 +1383,22 @@ func (c *SQLiteConn) RegisterFunc(name string, impl any, pure bool) error { if pure { opts |= C.SQLITE_DETERMINISTIC } - rv := sqlite3CreateFunction(c.db, cname, C.int(numArgs), C.int(opts), newHandle(c, &fi), C.callbackTrampoline, nil, nil) + rv := C.sqlite3_create_function( + c.db, + cname, + C.int(numArgs), + C.int(opts), + newHandle(c, &fi), + (*[0]byte)(C.callbackTrampoline), + nil, + nil, + ) if rv != C.SQLITE_OK { return c.lastError() } return nil } -func sqlite3CreateFunction(db *C.sqlite3, zFunctionName *C.char, nArg C.int, eTextRep C.int, pApp unsafe.Pointer, xFunc unsafe.Pointer, xStep unsafe.Pointer, xFinal unsafe.Pointer) C.int { - return C._sqlite3_create_function(db, zFunctionName, nArg, eTextRep, C.uintptr_t(uintptr(pApp)), (*[0]byte)(xFunc), (*[0]byte)(xStep), (*[0]byte)(xFinal)) -} - // RegisterAggregator makes a Go type available as a SQLite aggregation function. // // Because aggregation is incremental, it's implemented in Go with a @@ -1917,7 +1517,16 @@ func (c *SQLiteConn) RegisterAggregator(name string, impl any, pure bool) error if pure { opts |= C.SQLITE_DETERMINISTIC } - rv := sqlite3CreateFunction(c.db, cname, C.int(stepNArgs), C.int(opts), newHandle(c, &ai), nil, C.stepTrampoline, C.doneTrampoline) + rv := C.sqlite3_create_function( + c.db, + cname, + C.int(stepNArgs), + C.int(opts), + newHandle(c, &ai), + nil, + (*[0]byte)(C.stepTrampoline), + (*[0]byte)(C.doneTrampoline), + ) if rv != C.SQLITE_OK { return c.lastError() } @@ -2449,9 +2058,13 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { vfs = C.CString(vfsName) defer C.free(unsafe.Pointer(vfs)) } - rv := C._sqlite3_open_v2(name, &db, - mutex|C.SQLITE_OPEN_READWRITE|C.SQLITE_OPEN_CREATE, - vfs) + + openFlags := + C.SQLITE_OPEN_READWRITE | + C.SQLITE_OPEN_CREATE | + C.SQLITE_OPEN_URI | + mutex + rv := C.sqlite3_open_v2(name, &db, openFlags, vfs) if rv != 0 { // Save off the error _before_ closing the database. // This is safe even if db is nil. @@ -2628,7 +2241,7 @@ func (c *SQLiteConn) prepare(ctx context.Context, query string) (driver.Stmt, er defer C.free(unsafe.Pointer(pquery)) var s *C.sqlite3_stmt var tail *C.char - rv := C._sqlite3_prepare_v2_internal(c.db, pquery, C.int(-1), &s, &tail) + rv := C.sqlite3_prepare_v2(c.db, pquery, C.int(-1), &s, &tail) if rv != C.SQLITE_OK { return nil, c.lastError() } @@ -2669,17 +2282,12 @@ func (c *SQLiteConn) GetFilename(schemaName string) string { return C.GoString(C.sqlite3_db_filename(c.db, C.CString(schemaName))) } -// GetLimit returns the current value of a run-time limit. -// See: sqlite3_limit, http://www.sqlite.org/c3ref/limit.html func (c *SQLiteConn) GetLimit(id int) int { - return int(C._sqlite3_limit(c.db, C.int(id), C.int(-1))) + return int(C.sqlite3_limit(c.db, C.int(id), C.int(-1))) } -// SetLimit changes the value of a run-time limits. -// Then this method returns the prior value of the limit. -// See: sqlite3_limit, http://www.sqlite.org/c3ref/limit.html func (c *SQLiteConn) SetLimit(id int, newVal int) int { - return int(C._sqlite3_limit(c.db, C.int(id), C.int(newVal))) + return int(C.sqlite3_limit(c.db, C.int(id), C.int(newVal))) } // SetFileControlInt invokes the xFileControl method on a given database. The @@ -2707,7 +2315,6 @@ func (c *SQLiteConn) SetFileControlInt(dbName string, op int, arg int) error { return nil } -// Close the statement. func (s *SQLiteStmt) Close() error { s.mu.Lock() defer s.mu.Unlock() @@ -2728,7 +2335,6 @@ func (s *SQLiteStmt) Close() error { return nil } -// NumInput return a number of parameters. func (s *SQLiteStmt) NumInput() int { return int(C.sqlite3_bind_parameter_count(s.s)) } @@ -2766,10 +2372,22 @@ func (s *SQLiteStmt) bind(args []driver.NamedValue) error { rv = C.sqlite3_bind_null(s.s, n) case string: if len(v) == 0 { - rv = C._sqlite3_bind_text(s.s, n, (*C.char)(unsafe.Pointer(&placeHolder[0])), C.int(0)) + rv = C.sqlite3_bind_text( + s.s, + n, + (*C.char)(unsafe.Pointer(&placeHolder[0])), + C.int(0), + C.SQLITE_TRANSIENT, + ) } else { b := []byte(v) - rv = C._sqlite3_bind_text(s.s, n, (*C.char)(unsafe.Pointer(&b[0])), C.int(len(b))) + rv = C.sqlite3_bind_text( + s.s, + n, + (*C.char)(unsafe.Pointer(&b[0])), + C.int(len(b)), + C.SQLITE_TRANSIENT, + ) } case int64: rv = C.sqlite3_bind_int64(s.s, n, C.sqlite3_int64(v)) @@ -2789,11 +2407,23 @@ func (s *SQLiteStmt) bind(args []driver.NamedValue) error { if ln == 0 { v = placeHolder } - rv = C._sqlite3_bind_blob(s.s, n, unsafe.Pointer(&v[0]), C.int(ln)) + rv = C.sqlite3_bind_blob( + s.s, + n, + unsafe.Pointer(&v[0]), + C.int(ln), + C.SQLITE_TRANSIENT, + ) } case time.Time: b := []byte(v.Format(SQLiteTimestampFormats[0])) - rv = C._sqlite3_bind_text(s.s, n, (*C.char)(unsafe.Pointer(&b[0])), C.int(len(b))) + rv = C.sqlite3_bind_text( + s.s, + n, + (*C.char)(unsafe.Pointer(&b[0])), + C.int(len(b)), + C.SQLITE_TRANSIENT, + ) } if rv != C.SQLITE_OK { return s.c.lastError() @@ -2905,8 +2535,7 @@ func (s *SQLiteStmt) execSync(args []driver.NamedValue) (driver.Result, error) { return nil, err } - var rowid, changes C.longlong - rv := C._sqlite3_step_row_internal(s.s, &rowid, &changes) + rv := C.sqlite3_step(s.s) if rv != C.SQLITE_ROW && rv != C.SQLITE_OK && rv != C.SQLITE_DONE { err := s.c.lastError() C.sqlite3_reset(s.s) @@ -2914,7 +2543,14 @@ func (s *SQLiteStmt) execSync(args []driver.NamedValue) (driver.Result, error) { return nil, err } - return &SQLiteResult{id: int64(rowid), changes: int64(changes)}, nil + db := C.sqlite3_db_handle(s.s) + id := int64(C.sqlite3_last_insert_rowid(db)) + changes := int64(C.sqlite3_changes(db)) + + return &SQLiteResult{ + id: id, + changes: changes, + }, nil } // Readonly reports if this statement is considered readonly by SQLite. @@ -2924,7 +2560,6 @@ func (s *SQLiteStmt) Readonly() bool { return C.sqlite3_stmt_readonly(s.s) == 1 } -// Close the rows. func (rc *SQLiteRows) Close() error { rc.s.mu.Lock() if rc.s.closed || rc.closed { @@ -2947,7 +2582,6 @@ func (rc *SQLiteRows) Close() error { return nil } -// Columns return column names. func (rc *SQLiteRows) Columns() []string { rc.s.mu.Lock() defer rc.s.mu.Unlock() @@ -3011,7 +2645,7 @@ func (rc *SQLiteRows) Next(dest []driver.Value) error { // nextSyncLocked moves cursor to next; must be called with locked mutex. func (rc *SQLiteRows) nextSyncLocked(dest []driver.Value) error { - rv := C._sqlite3_step_internal(rc.s.s) + rv := C.sqlite3_step(rc.s.s) if rv == C.SQLITE_DONE { return io.EOF } @@ -3120,7 +2754,12 @@ func (c *SQLiteContext) ResultBlob(b []byte) { if len(b) > 0 { p = &b[0] } - C.my_result_blob((*C.sqlite3_context)(c), unsafe.Pointer(p), C.int(len(b))) + C.sqlite3_result_blob( + (*C.sqlite3_context)(c), + unsafe.Pointer(p), + C.int(len(b)), + C.SQLITE_TRANSIENT, + ) } // ResultDouble sets the result of an SQL function. @@ -3156,7 +2795,12 @@ func (c *SQLiteContext) ResultNull() { func (c *SQLiteContext) ResultText(s string) { h := (*reflect.StringHeader)(unsafe.Pointer(&s)) cs, l := (*C.char)(unsafe.Pointer(h.Data)), C.int(h.Len) - C.my_result_text((*C.sqlite3_context)(c), cs, l) + C.sqlite3_result_text( + (*C.sqlite3_context)(c), + cs, + l, + C.SQLITE_TRANSIENT, + ) } // ResultZeroblob sets the result of an SQL function. @@ -3368,7 +3012,6 @@ func (t *unlock_notify_table) get(h uint) chan struct{} { return c } -//export unlock_notify_callback func unlock_notify_callback(argv unsafe.Pointer, argc C.int) { for i := 0; i < int(argc); i++ { parg := ((*(*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.uint)(nil))]*[1]uint)(argv))[i]) @@ -3379,46 +3022,6 @@ func unlock_notify_callback(argv unsafe.Pointer, argc C.int) { } } -//export unlock_notify_wait -func unlock_notify_wait(db *C.sqlite3) C.int { - // It has to be a bufferred channel to not block in sqlite_unlock_notify - // as sqlite_unlock_notify could invoke the callback before it returns. - c := make(chan struct{}, 1) - defer close(c) - - h := unt.add(c) - defer unt.remove(h) - - pargv := C.malloc(C.sizeof_uint) - defer C.free(pargv) - - argv := (*[1]uint)(pargv) - argv[0] = h - if rv := C.sqlite3_unlock_notify(db, (*[0]byte)(C.unlock_notify_callback), unsafe.Pointer(pargv)); rv != C.SQLITE_OK { - return rv - } - - <-c - - return C.SQLITE_OK -} - -type sqliteModule struct { - c *SQLiteConn - name string - module Module -} - -type sqliteVTab struct { - module *sqliteModule - vTab VTab -} - -type sqliteVTabCursor struct { - vTab *sqliteVTab - vTabCursor VTabCursor -} - // Op is type of operations. type Op uint8 @@ -3504,401 +3107,6 @@ type IndexResult struct { EstimatedRows float64 } -// mPrintf is a utility wrapper around sqlite3_mprintf -func mPrintf(format, arg string) *C.char { - cf := C.CString(format) - defer C.free(unsafe.Pointer(cf)) - ca := C.CString(arg) - defer C.free(unsafe.Pointer(ca)) - return C._sqlite3_mprintf(cf, ca) -} - -//export goMInit -func goMInit(db, pClientData unsafe.Pointer, argc C.int, argv **C.char, pzErr **C.char, isCreate C.int) C.uintptr_t { - m := lookupHandle(pClientData).(*sqliteModule) - if m.c.db != (*C.sqlite3)(db) { - *pzErr = mPrintf("%s", "Inconsistent db handles") - return 0 - } - args := make([]string, argc) - var A []*C.char - slice := reflect.SliceHeader{Data: uintptr(unsafe.Pointer(argv)), Len: int(argc), Cap: int(argc)} - a := reflect.NewAt(reflect.TypeOf(A), unsafe.Pointer(&slice)).Elem().Interface() - for i, s := range a.([]*C.char) { - args[i] = C.GoString(s) - } - var vTab VTab - var err error - if isCreate == 1 { - vTab, err = m.module.Create(m.c, args) - } else { - vTab, err = m.module.Connect(m.c, args) - } - - if err != nil { - *pzErr = mPrintf("%s", err.Error()) - return 0 - } - vt := sqliteVTab{m, vTab} - *pzErr = nil - return C.uintptr_t(uintptr(newHandle(m.c, &vt))) -} - -//export goVRelease -func goVRelease(pVTab unsafe.Pointer, isDestroy C.int) *C.char { - vt := lookupHandle(pVTab).(*sqliteVTab) - var err error - if isDestroy == 1 { - err = vt.vTab.Destroy() - } else { - err = vt.vTab.Disconnect() - } - if err != nil { - return mPrintf("%s", err.Error()) - } - return nil -} - -//export goVOpen -func goVOpen(pVTab unsafe.Pointer, pzErr **C.char) C.uintptr_t { - vt := lookupHandle(pVTab).(*sqliteVTab) - vTabCursor, err := vt.vTab.Open() - if err != nil { - *pzErr = mPrintf("%s", err.Error()) - return 0 - } - vtc := sqliteVTabCursor{vt, vTabCursor} - *pzErr = nil - return C.uintptr_t(uintptr(newHandle(vt.module.c, &vtc))) -} - -//export goVBestIndex -func goVBestIndex(pVTab unsafe.Pointer, icp unsafe.Pointer) *C.char { - vt := lookupHandle(pVTab).(*sqliteVTab) - info := (*C.sqlite3_index_info)(icp) - csts := constraints(info) - res, err := vt.vTab.BestIndex(csts, orderBys(info)) - if err != nil { - return mPrintf("%s", err.Error()) - } - if len(res.Used) != len(csts) { - return mPrintf("Result.Used != expected value", "") - } - - // Get a pointer to constraint_usage struct so we can update in place. - - slice := *(*[]C.struct_sqlite3_index_constraint_usage)(unsafe.Pointer(&reflect.SliceHeader{ - Data: uintptr(unsafe.Pointer(info.aConstraintUsage)), - Len: int(info.nConstraint), - Cap: int(info.nConstraint), - })) - index := 1 - for i := range slice { - if res.Used[i] { - slice[i].argvIndex = C.int(index) - slice[i].omit = C.uchar(1) - index++ - } - } - - info.idxNum = C.int(res.IdxNum) - info.idxStr = (*C.char)(C.sqlite3_malloc(C.int(len(res.IdxStr) + 1))) - if info.idxStr == nil { - // C.malloc and C.CString ordinarily do this for you. See https://golang.org/cmd/cgo/ - panic("out of memory") - } - info.needToFreeIdxStr = C.int(1) - - idxStr := *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{ - Data: uintptr(unsafe.Pointer(info.idxStr)), - Len: len(res.IdxStr) + 1, - Cap: len(res.IdxStr) + 1, - })) - copy(idxStr, res.IdxStr) - idxStr[len(idxStr)-1] = 0 // null-terminated string - - if res.AlreadyOrdered { - info.orderByConsumed = C.int(1) - } - info.estimatedCost = C.double(res.EstimatedCost) - info.estimatedRows = C.sqlite3_int64(res.EstimatedRows) - - return nil -} - -//export goVClose -func goVClose(pCursor unsafe.Pointer) *C.char { - vtc := lookupHandle(pCursor).(*sqliteVTabCursor) - err := vtc.vTabCursor.Close() - if err != nil { - return mPrintf("%s", err.Error()) - } - return nil -} - -//export goMDestroy -func goMDestroy(pClientData unsafe.Pointer) { - m := lookupHandle(pClientData).(*sqliteModule) - m.module.DestroyModule() -} - -//export goVFilter -func goVFilter(pCursor unsafe.Pointer, idxNum C.int, idxName *C.char, argc C.int, argv **C.sqlite3_value) *C.char { - vtc := lookupHandle(pCursor).(*sqliteVTabCursor) - args := (*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.sqlite3_value)(nil))]*C.sqlite3_value)(unsafe.Pointer(argv))[:argc:argc] - vals := make([]any, 0, argc) - for _, v := range args { - conv, err := callbackArgGeneric(v) - if err != nil { - return mPrintf("%s", err.Error()) - } - vals = append(vals, conv.Interface()) - } - err := vtc.vTabCursor.Filter(int(idxNum), C.GoString(idxName), vals) - if err != nil { - return mPrintf("%s", err.Error()) - } - return nil -} - -//export goVNext -func goVNext(pCursor unsafe.Pointer) *C.char { - vtc := lookupHandle(pCursor).(*sqliteVTabCursor) - err := vtc.vTabCursor.Next() - if err != nil { - return mPrintf("%s", err.Error()) - } - return nil -} - -//export goVEof -func goVEof(pCursor unsafe.Pointer) C.int { - vtc := lookupHandle(pCursor).(*sqliteVTabCursor) - err := vtc.vTabCursor.EOF() - if err { - return 1 - } - return 0 -} - -//export goVColumn -func goVColumn(pCursor, cp unsafe.Pointer, col C.int) *C.char { - vtc := lookupHandle(pCursor).(*sqliteVTabCursor) - c := (*SQLiteContext)(cp) - err := vtc.vTabCursor.Column(c, int(col)) - if err != nil { - return mPrintf("%s", err.Error()) - } - return nil -} - -//export goVRowid -func goVRowid(pCursor unsafe.Pointer, pRowid *C.sqlite3_int64) *C.char { - vtc := lookupHandle(pCursor).(*sqliteVTabCursor) - rowid, err := vtc.vTabCursor.Rowid() - if err != nil { - return mPrintf("%s", err.Error()) - } - *pRowid = C.sqlite3_int64(rowid) - return nil -} - -//export goVUpdate -func goVUpdate(pVTab unsafe.Pointer, argc C.int, argv **C.sqlite3_value, pRowid *C.sqlite3_int64) *C.char { - vt := lookupHandle(pVTab).(*sqliteVTab) - - var tname string - if n, ok := vt.vTab.(interface { - TableName() string - }); ok { - tname = n.TableName() + " " - } - - err := fmt.Errorf("virtual %s table %sis read-only", vt.module.name, tname) - if v, ok := vt.vTab.(VTabUpdater); ok { - // convert argv - args := (*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.sqlite3_value)(nil))]*C.sqlite3_value)(unsafe.Pointer(argv))[:argc:argc] - vals := make([]any, 0, argc) - for _, v := range args { - conv, err := callbackArgGeneric(v) - if err != nil { - return mPrintf("%s", err.Error()) - } - - // work around for SQLITE_NULL - x := conv.Interface() - if z, ok := x.([]byte); ok && z == nil { - x = nil - } - - vals = append(vals, x) - } - - switch { - case argc == 1: - err = v.Delete(vals[0]) - - case argc > 1 && vals[0] == nil: - var id int64 - id, err = v.Insert(vals[1], vals[2:]) - if err == nil { - *pRowid = C.sqlite3_int64(id) - } - - case argc > 1: - err = v.Update(vals[1], vals[2:]) - } - } - - if err != nil { - return mPrintf("%s", err.Error()) - } - - return nil -} - -// Module is a "virtual table module", it defines the implementation of a -// virtual tables. See: http://sqlite.org/c3ref/module.html -type Module interface { - // http://sqlite.org/vtab.html#xcreate - Create(c *SQLiteConn, args []string) (VTab, error) - // http://sqlite.org/vtab.html#xconnect - Connect(c *SQLiteConn, args []string) (VTab, error) - // http://sqlite.org/c3ref/create_module.html - DestroyModule() -} - -// EponymousOnlyModule is a "virtual table module" (as above), but -// for defining "eponymous only" virtual tables See: https://sqlite.org/vtab.html#eponymous_only_virtual_tables -type EponymousOnlyModule interface { - Module - EponymousOnlyModule() -} - -// VTab describes a particular instance of the virtual table. -// See: http://sqlite.org/c3ref/vtab.html -type VTab interface { - // http://sqlite.org/vtab.html#xbestindex - BestIndex([]InfoConstraint, []InfoOrderBy) (*IndexResult, error) - // http://sqlite.org/vtab.html#xdisconnect - Disconnect() error - // http://sqlite.org/vtab.html#sqlite3_module.xDestroy - Destroy() error - // http://sqlite.org/vtab.html#xopen - Open() (VTabCursor, error) -} - -// VTabUpdater is a type that allows a VTab to be inserted, updated, or -// deleted. -// See: https://sqlite.org/vtab.html#xupdate -type VTabUpdater interface { - Delete(any) error - Insert(any, []any) (int64, error) - Update(any, []any) error -} - -// VTabCursor describes cursors that point into the virtual table and are used -// to loop through the virtual table. See: http://sqlite.org/c3ref/vtab_cursor.html -type VTabCursor interface { - // http://sqlite.org/vtab.html#xclose - Close() error - // http://sqlite.org/vtab.html#xfilter - Filter(idxNum int, idxStr string, vals []any) error - // http://sqlite.org/vtab.html#xnext - Next() error - // http://sqlite.org/vtab.html#xeof - EOF() bool - // http://sqlite.org/vtab.html#xcolumn - Column(c *SQLiteContext, col int) error - // http://sqlite.org/vtab.html#xrowid - Rowid() (int64, error) -} - -// DeclareVTab declares the Schema of a virtual table. -// See: http://sqlite.org/c3ref/declare_vtab.html -func (c *SQLiteConn) DeclareVTab(sql string) error { - zSQL := C.CString(sql) - defer C.free(unsafe.Pointer(zSQL)) - rv := C.sqlite3_declare_vtab(c.db, zSQL) - if rv != C.SQLITE_OK { - return c.lastError() - } - return nil -} - -// CreateModule registers a virtual table implementation. -// See: http://sqlite.org/c3ref/create_module.html -func (c *SQLiteConn) CreateModule(moduleName string, module Module) error { - mname := C.CString(moduleName) - defer C.free(unsafe.Pointer(mname)) - udm := sqliteModule{c, moduleName, module} - switch module.(type) { - case EponymousOnlyModule: - rv := C._sqlite3_create_module_eponymous_only(c.db, mname, C.uintptr_t(uintptr(newHandle(c, &udm)))) - if rv != C.SQLITE_OK { - return c.lastError() - } - return nil - case Module: - rv := C._sqlite3_create_module(c.db, mname, C.uintptr_t(uintptr(newHandle(c, &udm)))) - if rv != C.SQLITE_OK { - return c.lastError() - } - return nil - } - return nil -} - -// Trace... constants identify the possible events causing callback invocation. -// Values are same as the corresponding SQLite Trace Event Codes. -const ( - TraceStmt = uint32(C.SQLITE_TRACE_STMT) - TraceProfile = uint32(C.SQLITE_TRACE_PROFILE) - TraceRow = uint32(C.SQLITE_TRACE_ROW) - TraceClose = uint32(C.SQLITE_TRACE_CLOSE) -) - -type TraceInfo struct { - // Pack together the shorter fields, to keep the struct smaller. - // On a 64-bit machine there would be padding - // between EventCode and ConnHandle; having AutoCommit here is "free": - EventCode uint32 - AutoCommit bool - ConnHandle uintptr - - // Usually filled, unless EventCode = TraceClose = SQLITE_TRACE_CLOSE: - // identifier for a prepared statement: - StmtHandle uintptr - - // Two strings filled when EventCode = TraceStmt = SQLITE_TRACE_STMT: - // (1) either the unexpanded SQL text of the prepared statement, or - // an SQL comment that indicates the invocation of a trigger; - // (2) expanded SQL, if requested and if (1) is not an SQL comment. - StmtOrTrigger string - ExpandedSQL string // only if requested (TraceConfig.WantExpandedSQL = true) - - // filled when EventCode = TraceProfile = SQLITE_TRACE_PROFILE: - // estimated number of nanoseconds that the prepared statement took to run: - RunTimeNanosec int64 - - DBError Error -} - -// TraceUserCallback gives the signature for a trace function -// provided by the user (Go application programmer). -// SQLite 3.14 documentation (as of September 2, 2016) -// for SQL Trace Hook = sqlite3_trace_v2(): -// The integer return value from the callback is currently ignored, -// though this may change in future releases. Callback implementations -// should return zero to ensure future compatibility. -type TraceUserCallback func(TraceInfo) int - -type TraceConfig struct { - Callback TraceUserCallback - EventMask uint32 - WantExpandedSQL bool -} - func fillDBError(dbErr *Error, db *C.sqlite3) { // See SQLiteConn.lastError(), in file 'sqlite3.go' at the time of writing (Sept 5, 2016) dbErr.Code = ErrNo(C.sqlite3_errcode(db)) @@ -3906,209 +3114,6 @@ func fillDBError(dbErr *Error, db *C.sqlite3) { dbErr.err = C.GoString(C.sqlite3_errmsg(db)) } -func fillExpandedSQL(info *TraceInfo, db *C.sqlite3, pStmt unsafe.Pointer) { - if pStmt == nil { - panic("No SQLite statement pointer in P arg of trace_v2 callback") - } - - expSQLiteCStr := C.sqlite3_expanded_sql((*C.sqlite3_stmt)(pStmt)) - defer C.sqlite3_free(unsafe.Pointer(expSQLiteCStr)) - if expSQLiteCStr == nil { - fillDBError(&info.DBError, db) - return - } - info.ExpandedSQL = C.GoString(expSQLiteCStr) -} - -//export traceCallbackTrampoline -func traceCallbackTrampoline( - traceEventCode C.uint, - // Parameter named 'C' in SQLite docs = Context given at registration: - ctx unsafe.Pointer, - // Parameter named 'P' in SQLite docs (Primary event data?): - p unsafe.Pointer, - // Parameter named 'X' in SQLite docs (eXtra event data?): - xValue unsafe.Pointer) C.int { - - eventCode := uint32(traceEventCode) - - if ctx == nil { - panic(fmt.Sprintf("No context (ev 0x%x)", traceEventCode)) - } - - contextDB := (*C.sqlite3)(ctx) - connHandle := uintptr(ctx) - - var traceConf TraceConfig - var found bool - if eventCode == TraceClose { - // clean up traceMap: 'pop' means get and delete - traceConf, found = popTraceMapping(connHandle) - } else { - traceConf, found = lookupTraceMapping(connHandle) - } - - if !found { - panic(fmt.Sprintf("Mapping not found for handle 0x%x (ev 0x%x)", - connHandle, eventCode)) - } - - var info TraceInfo - - info.EventCode = eventCode - info.AutoCommit = (int(C.sqlite3_get_autocommit(contextDB)) != 0) - info.ConnHandle = connHandle - - switch eventCode { - case TraceStmt: - info.StmtHandle = uintptr(p) - - var xStr string - if xValue != nil { - xStr = C.GoString((*C.char)(xValue)) - } - info.StmtOrTrigger = xStr - if !strings.HasPrefix(xStr, "--") { - // Not SQL comment, therefore the current event - // is not related to a trigger. - // The user might want to receive the expanded SQL; - // let's check: - if traceConf.WantExpandedSQL { - fillExpandedSQL(&info, contextDB, p) - } - } - - case TraceProfile: - info.StmtHandle = uintptr(p) - - if xValue == nil { - panic("NULL pointer in X arg of trace_v2 callback for SQLITE_TRACE_PROFILE event") - } - - info.RunTimeNanosec = *(*int64)(xValue) - - // sample the error //TODO: is it safe? is it useful? - fillDBError(&info.DBError, contextDB) - - case TraceRow: - info.StmtHandle = uintptr(p) - - case TraceClose: - handle := uintptr(p) - if handle != info.ConnHandle { - panic(fmt.Sprintf("Different conn handle 0x%x (expected 0x%x) in SQLITE_TRACE_CLOSE event.", - handle, info.ConnHandle)) - } - - default: - // Pass unsupported events to the user callback (if configured); - // let the user callback decide whether to panic or ignore them. - } - - // Do not execute user callback when the event was not requested by user! - // Remember that the Close event is always selected when - // registering this callback trampoline with SQLite --- for cleanup. - // In the future there may be more events forced to "selected" in SQLite - // for the driver's needs. - if traceConf.EventMask&eventCode == 0 { - return 0 - } - - r := 0 - if traceConf.Callback != nil { - r = traceConf.Callback(info) - } - return C.int(r) -} - -type traceMapEntry struct { - config TraceConfig -} - -var traceMapLock sync.Mutex -var traceMap = make(map[uintptr]traceMapEntry) - -func addTraceMapping(connHandle uintptr, traceConf TraceConfig) { - traceMapLock.Lock() - defer traceMapLock.Unlock() - - oldEntryCopy, found := traceMap[connHandle] - if found { - panic(fmt.Sprintf("Adding trace config %v: handle 0x%x already registered (%v).", - traceConf, connHandle, oldEntryCopy.config)) - } - traceMap[connHandle] = traceMapEntry{config: traceConf} -} - -func lookupTraceMapping(connHandle uintptr) (TraceConfig, bool) { - traceMapLock.Lock() - defer traceMapLock.Unlock() - - entryCopy, found := traceMap[connHandle] - return entryCopy.config, found -} - -// 'pop' = get and delete from map before returning the value to the caller -func popTraceMapping(connHandle uintptr) (TraceConfig, bool) { - traceMapLock.Lock() - defer traceMapLock.Unlock() - - entryCopy, found := traceMap[connHandle] - if found { - delete(traceMap, connHandle) - } - return entryCopy.config, found -} - -// SetTrace installs or removes the trace callback for the given database connection. -// It's not named 'RegisterTrace' because only one callback can be kept and called. -// Calling SetTrace a second time on same database connection -// overrides (cancels) any prior callback and all its settings: -// event mask, etc. -func (c *SQLiteConn) SetTrace(requested *TraceConfig) error { - connHandle := uintptr(unsafe.Pointer(c.db)) - - _, _ = popTraceMapping(connHandle) - - if requested == nil { - // The traceMap entry was deleted already by popTraceMapping(): - // can disable all events now, no need to watch for TraceClose. - err := c.setSQLiteTrace(0) - return err - } - - reqCopy := *requested - - // Disable potentially expensive operations - // if their result will not be used. We are doing this - // just in case the caller provided nonsensical input. - if reqCopy.EventMask&TraceStmt == 0 { - reqCopy.WantExpandedSQL = false - } - - addTraceMapping(connHandle, reqCopy) - - // The callback trampoline function does cleanup on Close event, - // regardless of the presence or absence of the user callback. - // Therefore it needs the Close event to be selected: - actualEventMask := uint(reqCopy.EventMask | TraceClose) - err := c.setSQLiteTrace(actualEventMask) - return err -} - -func (c *SQLiteConn) setSQLiteTrace(sqliteEventMask uint) error { - rv := C.sqlite3_trace_v2(c.db, - C.uint(sqliteEventMask), - (*[0]byte)(unsafe.Pointer(C.traceCallbackTrampoline)), - unsafe.Pointer(c.db)) // Fourth arg is same as first: we are - // passing the database connection handle as callback context. - - if rv != C.SQLITE_OK { - return c.lastError() - } - return nil -} - // ColumnTypeDatabaseTypeName implement RowsColumnTypeDatabaseTypeName. func (rc *SQLiteRows) ColumnTypeDatabaseTypeName(i int) string { return C.GoString(C.sqlite3_column_decltype(rc.s.s, C.int(i))) |