aboutsummaryrefslogtreecommitdiff
path: root/src/golite.go
diff options
context:
space:
mode:
authorEuAndreh <eu@euandre.org>2024-10-01 07:47:29 -0300
committerEuAndreh <eu@euandre.org>2024-10-01 07:49:17 -0300
commitdf51f67c058f4b0832236c91abfdc8f9485e6ff9 (patch)
tree72f79684a0c8609d7a5e84b14e40c18902b03fd0 /src/golite.go
parentsrc/golite.go: Update doccomments on Open() (diff)
downloadgolite-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.go1195
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)))