aboutsummaryrefslogtreecommitdiff
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
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
-rw-r--r--src/golite.go1195
-rw-r--r--tests/golite.go578
2 files changed, 100 insertions, 1673 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)))
diff --git a/tests/golite.go b/tests/golite.go
index 1cb9436..682cead 100644
--- a/tests/golite.go
+++ b/tests/golite.go
@@ -1842,298 +1842,6 @@ func TestUnlockNotifyDeadlock(t *testing.T) {
wg.Wait()
}
-type testModule struct {
- t *testing.T
- intarray []int
-}
-
-type testVTab struct {
- intarray []int
-}
-
-type testVTabCursor struct {
- vTab *testVTab
- index int
-}
-
-func (m testModule) Create(c *SQLiteConn, args []string) (VTab, error) {
- if len(args) != 6 {
- m.t.Fatal("six arguments expected")
- }
- if args[0] != "test" {
- m.t.Fatal("module name")
- }
- if args[1] != "main" {
- m.t.Fatal("db name")
- }
- if args[2] != "vtab" {
- m.t.Fatal("table name")
- }
- if args[3] != "'1'" {
- m.t.Fatal("first arg")
- }
- if args[4] != "2" {
- m.t.Fatal("second arg")
- }
- if args[5] != "three" {
- m.t.Fatal("third argsecond arg")
- }
- err := c.DeclareVTab("CREATE TABLE x(test TEXT)")
- if err != nil {
- return nil, err
- }
- return &testVTab{m.intarray}, nil
-}
-
-func (m testModule) Connect(c *SQLiteConn, args []string) (VTab, error) {
- return m.Create(c, args)
-}
-
-func (m testModule) DestroyModule() {}
-
-func (v *testVTab) BestIndex(cst []InfoConstraint, ob []InfoOrderBy) (*IndexResult, error) {
- used := make([]bool, 0, len(cst))
- for range cst {
- used = append(used, false)
- }
- return &IndexResult{
- Used: used,
- IdxNum: 0,
- IdxStr: "test-index",
- AlreadyOrdered: true,
- EstimatedCost: 100,
- EstimatedRows: 200,
- }, nil
-}
-
-func (v *testVTab) Disconnect() error {
- return nil
-}
-
-func (v *testVTab) Destroy() error {
- return nil
-}
-
-func (v *testVTab) Open() (VTabCursor, error) {
- return &testVTabCursor{v, 0}, nil
-}
-
-func (vc *testVTabCursor) Close() error {
- return nil
-}
-
-func (vc *testVTabCursor) Filter(idxNum int, idxStr string, vals []any) error {
- vc.index = 0
- return nil
-}
-
-func (vc *testVTabCursor) Next() error {
- vc.index++
- return nil
-}
-
-func (vc *testVTabCursor) EOF() bool {
- return vc.index >= len(vc.vTab.intarray)
-}
-
-func (vc *testVTabCursor) Column(c *SQLiteContext, col int) error {
- if col != 0 {
- return fmt.Errorf("column index out of bounds: %d", col)
- }
- c.ResultInt(vc.vTab.intarray[vc.index])
- return nil
-}
-
-func (vc *testVTabCursor) Rowid() (int64, error) {
- return int64(vc.index), nil
-}
-
-func TestCreateModule(t *testing.T) {
- intarray := []int{1, 2, 3}
- sql.Register("sqlite3_TestCreateModule", &SQLiteDriver{
- ConnectHook: func(conn *SQLiteConn) error {
- return conn.CreateModule("test", testModule{t, intarray})
- },
- })
- db, err := sql.Open("sqlite3_TestCreateModule", ":memory:")
- if err != nil {
- t.Fatalf("could not open db: %v", err)
- }
- _, err = db.Exec("CREATE VIRTUAL TABLE vtab USING test('1', 2, three)")
- if err != nil {
- t.Fatalf("could not create vtable: %v", err)
- }
-
- var i, value int
- rows, err := db.Query("SELECT rowid, * FROM vtab WHERE test = '3'")
- if err != nil {
- t.Fatalf("couldn't select from virtual table: %v", err)
- }
- for rows.Next() {
- rows.Scan(&i, &value)
- if intarray[i] != value {
- t.Fatalf("want %v but %v", intarray[i], value)
- }
- }
-
- _, err = db.Exec("DROP TABLE vtab")
- if err != nil {
- t.Fatalf("couldn't drop virtual table: %v", err)
- }
-}
-
-func TestVUpdate(t *testing.T) {
- // create module
- updateMod := &vtabUpdateModule{t, make(map[string]*vtabUpdateTable)}
-
- // register module
- sql.Register("sqlite3_TestVUpdate", &SQLiteDriver{
- ConnectHook: func(conn *SQLiteConn) error {
- return conn.CreateModule("updatetest", updateMod)
- },
- })
-
- // connect
- db, err := sql.Open("sqlite3_TestVUpdate", ":memory:")
- if err != nil {
- t.Fatalf("could not open db: %v", err)
- }
-
- // create test table
- _, err = db.Exec(`CREATE VIRTUAL TABLE vt USING updatetest(f1 integer, f2 text, f3 text)`)
- if err != nil {
- t.Fatalf("could not create updatetest vtable vt, got: %v", err)
- }
-
- // check that table is defined properly
- if len(updateMod.tables) != 1 {
- t.Fatalf("expected exactly 1 table to exist, got: %d", len(updateMod.tables))
- }
- if _, ok := updateMod.tables["vt"]; !ok {
- t.Fatalf("expected table `vt` to exist in tables")
- }
-
- // check nothing in updatetest
- rows, err := db.Query(`select * from vt`)
- if err != nil {
- t.Fatalf("could not query vt, got: %v", err)
- }
- i, err := getRowCount(rows)
- if err != nil {
- t.Fatalf("expected no error, got: %v", err)
- }
- if i != 0 {
- t.Fatalf("expected no rows in vt, got: %d", i)
- }
-
- _, err = db.Exec(`delete from vt where f1 = 'yes'`)
- if err != nil {
- t.Fatalf("expected error on delete, got nil")
- }
-
- // test bad column name
- _, err = db.Exec(`insert into vt (f4) values('a')`)
- if err == nil {
- t.Fatalf("expected error on insert, got nil")
- }
-
- // insert to vt
- res, err := db.Exec(`insert into vt (f1, f2, f3) values (115, 'b', 'c'), (116, 'd', 'e')`)
- if err != nil {
- t.Fatalf("expected no error on insert, got: %v", err)
- }
- n, err := res.RowsAffected()
- if err != nil {
- t.Fatalf("expected no error, got: %v", err)
- }
- if n != 2 {
- t.Fatalf("expected 1 row affected, got: %d", n)
- }
-
- // check vt table
- vt := updateMod.tables["vt"]
- if len(vt.data) != 2 {
- t.Fatalf("expected table vt to have exactly 2 rows, got: %d", len(vt.data))
- }
- if !reflect.DeepEqual(vt.data[0], []any{int64(115), "b", "c"}) {
- t.Fatalf("expected table vt entry 0 to be [115 b c], instead: %v", vt.data[0])
- }
- if !reflect.DeepEqual(vt.data[1], []any{int64(116), "d", "e"}) {
- t.Fatalf("expected table vt entry 1 to be [116 d e], instead: %v", vt.data[1])
- }
-
- // query vt
- var f1 int
- var f2, f3 string
- err = db.QueryRow(`select * from vt where f1 = 115`).Scan(&f1, &f2, &f3)
- if err != nil {
- t.Fatalf("expected no error on vt query, got: %v", err)
- }
-
- // check column values
- if f1 != 115 || f2 != "b" || f3 != "c" {
- t.Errorf("expected f1==115, f2==b, f3==c, got: %d, %q, %q", f1, f2, f3)
- }
-
- // update vt
- res, err = db.Exec(`update vt set f1=117, f2='f' where f3='e'`)
- if err != nil {
- t.Fatalf("expected no error, got: %v", err)
- }
- n, err = res.RowsAffected()
- if err != nil {
- t.Fatalf("expected no error, got: %v", err)
- }
- if n != 1 {
- t.Fatalf("expected exactly one row updated, got: %d", n)
- }
-
- // check vt table
- if len(vt.data) != 2 {
- t.Fatalf("expected table vt to have exactly 2 rows, got: %d", len(vt.data))
- }
- if !reflect.DeepEqual(vt.data[0], []any{int64(115), "b", "c"}) {
- t.Fatalf("expected table vt entry 0 to be [115 b c], instead: %v", vt.data[0])
- }
- if !reflect.DeepEqual(vt.data[1], []any{int64(117), "f", "e"}) {
- t.Fatalf("expected table vt entry 1 to be [117 f e], instead: %v", vt.data[1])
- }
-
- // delete from vt
- res, err = db.Exec(`delete from vt where f1 = 117`)
- if err != nil {
- t.Fatalf("expected no error, got: %v", err)
- }
- n, err = res.RowsAffected()
- if err != nil {
- t.Fatalf("expected no error, got: %v", err)
- }
- if n != 1 {
- t.Fatalf("expected exactly one row deleted, got: %d", n)
- }
-
- // check vt table
- if len(vt.data) != 1 {
- t.Fatalf("expected table vt to have exactly 1 row, got: %d", len(vt.data))
- }
- if !reflect.DeepEqual(vt.data[0], []any{int64(115), "b", "c"}) {
- t.Fatalf("expected table vt entry 0 to be [115 b c], instead: %v", vt.data[0])
- }
-
- // check updatetest has 1 result
- rows, err = db.Query(`select * from vt`)
- if err != nil {
- t.Fatalf("could not query vt, got: %v", err)
- }
- i, err = getRowCount(rows)
- if err != nil {
- t.Fatalf("expected no error, got: %v", err)
- }
- if i != 1 {
- t.Fatalf("expected 1 row in vt, got: %d", i)
- }
-}
-
func getRowCount(rows *sql.Rows) (int, error) {
var i int
for rows.Next() {
@@ -2142,289 +1850,6 @@ func getRowCount(rows *sql.Rows) (int, error) {
return i, nil
}
-type vtabUpdateModule struct {
- t *testing.T
- tables map[string]*vtabUpdateTable
-}
-
-func (m *vtabUpdateModule) Create(c *SQLiteConn, args []string) (VTab, error) {
- if len(args) < 2 {
- return nil, errors.New("must declare at least one column")
- }
-
- // get database name, table name, and column declarations ...
- dbname, tname, decls := args[1], args[2], args[3:]
-
- // extract column names + types from parameters declarations
- cols, typs := make([]string, len(decls)), make([]string, len(decls))
- for i := 0; i < len(decls); i++ {
- n, typ := decls[i], ""
- if j := strings.IndexAny(n, " \t\n"); j != -1 {
- typ, n = strings.TrimSpace(n[j+1:]), n[:j]
- }
- cols[i], typs[i] = n, typ
- }
-
- // declare table
- err := c.DeclareVTab(fmt.Sprintf(`CREATE TABLE "%s"."%s" (%s)`, dbname, tname, strings.Join(decls, ",")))
- if err != nil {
- return nil, err
- }
-
- // create table
- vtab := &vtabUpdateTable{m.t, dbname, tname, cols, typs, make([][]any, 0)}
- m.tables[tname] = vtab
- return vtab, nil
-}
-
-func (m *vtabUpdateModule) Connect(c *SQLiteConn, args []string) (VTab, error) {
- return m.Create(c, args)
-}
-
-func (m *vtabUpdateModule) DestroyModule() {}
-
-type vtabUpdateTable struct {
- t *testing.T
- db string
- name string
- cols []string
- typs []string
- data [][]any
-}
-
-func (t *vtabUpdateTable) Open() (VTabCursor, error) {
- return &vtabUpdateCursor{t, 0}, nil
-}
-
-func (t *vtabUpdateTable) BestIndex(cst []InfoConstraint, ob []InfoOrderBy) (*IndexResult, error) {
- return &IndexResult{Used: make([]bool, len(cst))}, nil
-}
-
-func (t *vtabUpdateTable) Disconnect() error {
- return nil
-}
-
-func (t *vtabUpdateTable) Destroy() error {
- return nil
-}
-
-func (t *vtabUpdateTable) Insert(id any, vals []any) (int64, error) {
- var i int64
- if id == nil {
- i, t.data = int64(len(t.data)), append(t.data, vals)
- return i, nil
- }
-
- var ok bool
- i, ok = id.(int64)
- if !ok {
- return 0, fmt.Errorf("id is invalid type: %T", id)
- }
-
- t.data[i] = vals
-
- return i, nil
-}
-
-func (t *vtabUpdateTable) Update(id any, vals []any) error {
- i, ok := id.(int64)
- if !ok {
- return fmt.Errorf("id is invalid type: %T", id)
- }
-
- if int(i) >= len(t.data) || i < 0 {
- return fmt.Errorf("invalid row id %d", i)
- }
-
- t.data[int(i)] = vals
-
- return nil
-}
-
-func (t *vtabUpdateTable) Delete(id any) error {
- i, ok := id.(int64)
- if !ok {
- return fmt.Errorf("id is invalid type: %T", id)
- }
-
- if int(i) >= len(t.data) || i < 0 {
- return fmt.Errorf("invalid row id %d", i)
- }
-
- t.data = append(t.data[:i], t.data[i+1:]...)
-
- return nil
-}
-
-type vtabUpdateCursor struct {
- t *vtabUpdateTable
- i int
-}
-
-func (c *vtabUpdateCursor) Column(ctxt *SQLiteContext, col int) error {
- switch x := c.t.data[c.i][col].(type) {
- case []byte:
- ctxt.ResultBlob(x)
- case bool:
- ctxt.ResultBool(x)
- case float64:
- ctxt.ResultDouble(x)
- case int:
- ctxt.ResultInt(x)
- case int64:
- ctxt.ResultInt64(x)
- case nil:
- ctxt.ResultNull()
- case string:
- ctxt.ResultText(x)
- default:
- ctxt.ResultText(fmt.Sprintf("%v", x))
- }
-
- return nil
-}
-
-func (c *vtabUpdateCursor) Filter(ixNum int, ixName string, vals []any) error {
- return nil
-}
-
-func (c *vtabUpdateCursor) Next() error {
- c.i++
- return nil
-}
-
-func (c *vtabUpdateCursor) EOF() bool {
- return c.i >= len(c.t.data)
-}
-
-func (c *vtabUpdateCursor) Rowid() (int64, error) {
- return int64(c.i), nil
-}
-
-func (c *vtabUpdateCursor) Close() error {
- return nil
-}
-
-type testModuleEponymousOnly struct {
- t *testing.T
- intarray []int
-}
-
-type testVTabEponymousOnly struct {
- intarray []int
-}
-
-type testVTabCursorEponymousOnly struct {
- vTab *testVTabEponymousOnly
- index int
-}
-
-func (m testModuleEponymousOnly) EponymousOnlyModule() {}
-
-func (m testModuleEponymousOnly) Create(c *SQLiteConn, args []string) (VTab, error) {
- err := c.DeclareVTab("CREATE TABLE x(test INT)")
- if err != nil {
- return nil, err
- }
- return &testVTabEponymousOnly{m.intarray}, nil
-}
-
-func (m testModuleEponymousOnly) Connect(c *SQLiteConn, args []string) (VTab, error) {
- return m.Create(c, args)
-}
-
-func (m testModuleEponymousOnly) DestroyModule() {}
-
-func (v *testVTabEponymousOnly) BestIndex(cst []InfoConstraint, ob []InfoOrderBy) (*IndexResult, error) {
- used := make([]bool, 0, len(cst))
- for range cst {
- used = append(used, false)
- }
- return &IndexResult{
- Used: used,
- IdxNum: 0,
- IdxStr: "test-index",
- AlreadyOrdered: true,
- EstimatedCost: 100,
- EstimatedRows: 200,
- }, nil
-}
-
-func (v *testVTabEponymousOnly) Disconnect() error {
- return nil
-}
-
-func (v *testVTabEponymousOnly) Destroy() error {
- return nil
-}
-
-func (v *testVTabEponymousOnly) Open() (VTabCursor, error) {
- return &testVTabCursorEponymousOnly{v, 0}, nil
-}
-
-func (vc *testVTabCursorEponymousOnly) Close() error {
- return nil
-}
-
-func (vc *testVTabCursorEponymousOnly) Filter(idxNum int, idxStr string, vals []any) error {
- vc.index = 0
- return nil
-}
-
-func (vc *testVTabCursorEponymousOnly) Next() error {
- vc.index++
- return nil
-}
-
-func (vc *testVTabCursorEponymousOnly) EOF() bool {
- return vc.index >= len(vc.vTab.intarray)
-}
-
-func (vc *testVTabCursorEponymousOnly) Column(c *SQLiteContext, col int) error {
- if col != 0 {
- return fmt.Errorf("column index out of bounds: %d", col)
- }
- c.ResultInt(vc.vTab.intarray[vc.index])
- return nil
-}
-
-func (vc *testVTabCursorEponymousOnly) Rowid() (int64, error) {
- return int64(vc.index), nil
-}
-
-func TestCreateModuleEponymousOnly(t *testing.T) {
- intarray := []int{1, 2, 3}
- sql.Register("sqlite3_TestCreateModuleEponymousOnly", &SQLiteDriver{
- ConnectHook: func(conn *SQLiteConn) error {
- return conn.CreateModule("test", testModuleEponymousOnly{t, intarray})
- },
- })
- db, err := sql.Open("sqlite3_TestCreateModuleEponymousOnly", ":memory:")
- if err != nil {
- t.Fatalf("could not open db: %v", err)
- }
-
- var i, value int
- rows, err := db.Query("SELECT rowid, * FROM test")
- if err != nil {
- t.Fatalf("couldn't select from virtual table: %v", err)
- }
- for rows.Next() {
- err := rows.Scan(&i, &value)
- if err != nil {
- t.Fatal(err)
- }
- if intarray[i] != value {
- t.Fatalf("want %v but %v", intarray[i], value)
- }
- }
-
- _, err = db.Exec("DROP TABLE test")
- if err != nil {
- t.Logf("couldn't drop virtual table: %v", err)
- }
-}
-
func TempFilename(t testing.TB) string {
f, err := ioutil.TempFile("", "go-sqlite3-test-")
if err != nil {
@@ -4873,9 +4298,6 @@ func MainTest() {
// { "TestUnlockNotify", TestUnlockNotify },
// { "TestUnlockNotifyMany", TestUnlockNotifyMany },
// { "TestUnlockNotifyDeadlock", TestUnlockNotifyDeadlock },
- { "TestCreateModule", TestCreateModule },
- { "TestVUpdate", TestVUpdate },
- { "TestCreateModuleEponymousOnly", TestCreateModuleEponymousOnly },
{ "TestOpen", TestOpen },
{ "TestOpenWithVFS", TestOpenWithVFS },
{ "TestOpenNoCreate", TestOpenNoCreate },