diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/golite.go | 5768 |
1 files changed, 5768 insertions, 0 deletions
diff --git a/src/golite.go b/src/golite.go index 2c52764..dbfade7 100644 --- a/src/golite.go +++ b/src/golite.go @@ -1,4 +1,5772 @@ package golite import ( + "context" + "crypto/sha1" + "crypto/sha256" + "crypto/sha512" + "database/sql" + "database/sql/driver" + "errors" + "fmt" + "io" + "math" + "net/url" + "reflect" + "runtime" + "strconv" + "strings" + "sync" + "time" + "unsafe" + "syscall" ) + +/* +// #ifndef USE_LIBSQLITE3 +// #include "sqlite3-binding.h" +// #else +// #endif + +#include <memory.h> +#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. + +// #ifndef USE_LIBSQLITE3 +// #include "sqlite3-binding.h" +// #else +// #include <sqlite3.h> +// #endif +// #include <stdlib.h> + +// void _sqlite3_result_text(sqlite3_context* ctx, const char* s); +// void _sqlite3_result_blob(sqlite3_context* ctx, const void* b, int l); + +// #ifndef USE_LIBSQLITE3 +// #include "sqlite3-binding.h" +// #else +// #include <sqlite3.h> +// #endif + +#cgo CFLAGS: -std=gnu99 +#cgo CFLAGS: -DSQLITE_ENABLE_RTREE +#cgo CFLAGS: -DSQLITE_THREADSAFE=1 +#cgo CFLAGS: -DHAVE_USLEEP=1 +#cgo CFLAGS: -DSQLITE_ENABLE_FTS3 +#cgo CFLAGS: -DSQLITE_ENABLE_FTS3_PARENTHESIS +#cgo CFLAGS: -DSQLITE_TRACE_SIZE_LIMIT=15 +#cgo CFLAGS: -DSQLITE_OMIT_DEPRECATED +#cgo CFLAGS: -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 +#cgo CFLAGS: -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT +#cgo CFLAGS: -Wno-deprecated-declarations +#cgo openbsd CFLAGS: -I/usr/local/include +#cgo openbsd LDFLAGS: -L/usr/local/lib + +// #ifndef USE_LIBSQLITE3 +// #include "sqlite3-binding.h" +// #else +// #include <sqlite3.h> +// #endif + +#ifdef __CYGWIN__ +# include <errno.h> +#endif + +#ifndef SQLITE_OPEN_READWRITE +# define SQLITE_OPEN_READWRITE 0 +#endif + +#ifndef SQLITE_OPEN_FULLMUTEX +# define SQLITE_OPEN_FULLMUTEX 0 +#endif + +#ifndef SQLITE_DETERMINISTIC +# define SQLITE_DETERMINISTIC 0 +#endif + +#if defined(HAVE_PREAD64) && defined(HAVE_PWRITE64) +# undef USE_PREAD +# undef USE_PWRITE +# define USE_PREAD64 1 +# define USE_PWRITE64 1 +#elif defined(HAVE_PREAD) && defined(HAVE_PWRITE) +# undef USE_PREAD +# undef USE_PWRITE +# define USE_PREAD64 1 +# define USE_PWRITE64 1 +#endif + +static int +_sqlite3_open_v2(const char *filename, sqlite3 **ppDb, int flags, const char *zVfs) { +#ifdef SQLITE_OPEN_URI + return sqlite3_open_v2(filename, ppDb, flags | SQLITE_OPEN_URI, zVfs); +#else + return sqlite3_open_v2(filename, ppDb, flags, zVfs); +#endif +} + +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; +} + +#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY +extern int _sqlite3_step_blocking(sqlite3_stmt *stmt); +extern int _sqlite3_step_row_blocking(sqlite3_stmt* stmt, long long* rowid, long long* changes); +extern int _sqlite3_prepare_v2_blocking(sqlite3 *db, const char *zSql, int nBytes, sqlite3_stmt **ppStmt, const char **pzTail); + +static int +_sqlite3_step_internal(sqlite3_stmt *stmt) +{ + return _sqlite3_step_blocking(stmt); +} + +static int +_sqlite3_step_row_internal(sqlite3_stmt* stmt, long long* rowid, long long* changes) +{ + return _sqlite3_step_row_blocking(stmt, rowid, changes); +} + +static int +_sqlite3_prepare_v2_internal(sqlite3 *db, const char *zSql, int nBytes, sqlite3_stmt **ppStmt, const char **pzTail) +{ + return _sqlite3_prepare_v2_blocking(db, zSql, nBytes, ppStmt, pzTail); +} + +#else +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); +} +#endif + +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); +} +#if 0 +#endif + + +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*); + +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*); + +#ifdef SQLITE_LIMIT_WORKER_THREADS +# define _SQLITE_HAS_LIMIT +# define SQLITE_LIMIT_LENGTH 0 +# define SQLITE_LIMIT_SQL_LENGTH 1 +# define SQLITE_LIMIT_COLUMN 2 +# define SQLITE_LIMIT_EXPR_DEPTH 3 +# define SQLITE_LIMIT_COMPOUND_SELECT 4 +# define SQLITE_LIMIT_VDBE_OP 5 +# define SQLITE_LIMIT_FUNCTION_ARG 6 +# define SQLITE_LIMIT_ATTACHED 7 +# define SQLITE_LIMIT_LIKE_PATTERN_LENGTH 8 +# define SQLITE_LIMIT_VARIABLE_NUMBER 9 +# define SQLITE_LIMIT_TRIGGER_DEPTH 10 +# define SQLITE_LIMIT_WORKER_THREADS 11 +# else +# define SQLITE_LIMIT_WORKER_THREADS 11 +#endif + +static int _sqlite3_limit(sqlite3* db, int limitId, int newLimit) { +#ifndef _SQLITE_HAS_LIMIT + return -1; +#else + return sqlite3_limit(db, limitId, newLimit); +#endif +} + +#if SQLITE_VERSION_NUMBER < 3012000 +static int sqlite3_system_errno(sqlite3 *db) { + return 0; +} +#endif + +// #ifndef USE_LIBSQLITE3 +// #include "sqlite3-binding.h" +// #else +// #include <sqlite3.h> +// #endif +// 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); +} + +#cgo CFLAGS: -DUSE_LIBSQLITE3 +#cgo linux LDFLAGS: -lsqlite3 +#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/sqlite/lib -lsqlite3 +#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/sqlite/include +#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/sqlite/lib -lsqlite3 +#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/sqlite/include +#cgo openbsd LDFLAGS: -lsqlite3 +#cgo solaris LDFLAGS: -lsqlite3 +#cgo windows LDFLAGS: -lsqlite3 +#cgo zos LDFLAGS: -lsqlite3 + +// #ifndef USE_LIBSQLITE3 +// #include "sqlite3-binding.h" +// #else +// #include <sqlite3.h> +// #endif + +#cgo CFLAGS: -DSQLITE_OMIT_LOAD_EXTENSION + +#cgo CFLAGS: -DSQLITE_ALLOW_URI_AUTHORITY +#cgo LDFLAGS: -lm + +#cgo CFLAGS: -DSQLITE_ENABLE_API_ARMOR +#cgo LDFLAGS: -lm + +// #ifndef USE_LIBSQLITE3 +// #cgo CFLAGS: -DSQLITE_ENABLE_COLUMN_METADATA +// #include <sqlite3-binding.h> +// #else +// #include <sqlite3.h> +// #endif + +#cgo CFLAGS: -DSQLITE_DEFAULT_FOREIGN_KEYS=1 +#cgo LDFLAGS: -lm + +#cgo CFLAGS: -DSQLITE_ENABLE_FTS5 +#cgo LDFLAGS: -lm + +#cgo LDFLAGS: -licuuc -licui18n +#cgo CFLAGS: -DSQLITE_ENABLE_ICU +#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/icu4c/include +#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/icu4c/lib +#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/icu4c/include +#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/icu4c/lib +#cgo openbsd LDFLAGS: -lsqlite3 + +#cgo CFLAGS: -DSQLITE_INTROSPECTION_PRAGMAS +#cgo LDFLAGS: -lm + +#cgo CFLAGS: -DSQLITE_ENABLE_MATH_FUNCTIONS +#cgo LDFLAGS: -lm + +#cgo CFLAGS: -DSQLITE_FORCE_OS_TRACE=1 +#cgo CFLAGS: -DSQLITE_DEBUG_OS_TRACE=1 + +#cgo CFLAGS: -DSQLITE_ENABLE_PREUPDATE_HOOK +#cgo LDFLAGS: -lm + +// #ifndef USE_LIBSQLITE3 +// #include "sqlite3-binding.h" +// #else +// #include <sqlite3.h> +// #endif + +// void preUpdateHookTrampoline(void*, sqlite3 *, int, char *, char *, sqlite3_int64, sqlite3_int64); + +#cgo CFLAGS: -DSQLITE_SECURE_DELETE=1 +#cgo LDFLAGS: -lm + +#cgo CFLAGS: -DSQLITE_SECURE_DELETE=FAST +#cgo LDFLAGS: -lm + +// #ifndef USE_LIBSQLITE3 +// #include <sqlite3-binding.h> +// #else +// #include <sqlite3.h> +// #endif + +#cgo CFLAGS: -DSQLITE_OMIT_DESERIALIZE + +#cgo CFLAGS: -DSQLITE_ENABLE_STAT4 +#cgo LDFLAGS: -lm + +#cgo CFLAGS: -DSQLITE_ENABLE_UNLOCK_NOTIFY + +// #include <stdlib.h> +// #include "sqlite3-binding.h" + +extern void unlock_notify_callback(void *arg, int argc); + +#cgo CFLAGS: -DSQLITE_USER_AUTHENTICATION +#cgo LDFLAGS: -lm +// #ifndef USE_LIBSQLITE3 +// #include "sqlite3-binding.h" +// #else +// #include <sqlite3.h> +// #endif + +// static int +// _sqlite3_user_authenticate(sqlite3* db, const char* zUsername, const char* aPW, int nPW) +// { +// return sqlite3_user_authenticate(db, zUsername, aPW, nPW); +// } +// +// static int +// _sqlite3_user_add(sqlite3* db, const char* zUsername, const char* aPW, int nPW, int isAdmin) +// { +// return sqlite3_user_add(db, zUsername, aPW, nPW, isAdmin); +// } +// +// static int +// _sqlite3_user_change(sqlite3* db, const char* zUsername, const char* aPW, int nPW, int isAdmin) +// { +// return sqlite3_user_change(db, zUsername, aPW, nPW, isAdmin); +// } +// +// static int +// _sqlite3_user_delete(sqlite3* db, const char* zUsername) +// { +// return sqlite3_user_delete(db, zUsername); +// } +// +// static int +// _sqlite3_auth_enabled(sqlite3* db) +// { +// int exists = -1; +// +// sqlite3_stmt *stmt; +// sqlite3_prepare_v2(db, "select count(type) from sqlite_master WHERE type='table' and name='sqlite_user';", -1, &stmt, NULL); +// +// while ( sqlite3_step(stmt) == SQLITE_ROW) { +// exists = sqlite3_column_int(stmt, 0); +// } +// +// sqlite3_finalize(stmt); +// +// return exists; +// } + +#cgo CFLAGS: -DSQLITE_DEFAULT_AUTOVACUUM=1 +#cgo LDFLAGS: -lm + +#cgo CFLAGS: -DSQLITE_DEFAULT_AUTOVACUUM=2 +#cgo LDFLAGS: -lm + +#cgo CFLAGS: -std=gnu99 +#cgo CFLAGS: -DSQLITE_ENABLE_RTREE +#cgo CFLAGS: -DSQLITE_THREADSAFE +#cgo CFLAGS: -DSQLITE_ENABLE_FTS3 +#cgo CFLAGS: -DSQLITE_ENABLE_FTS3_PARENTHESIS +#cgo CFLAGS: -DSQLITE_ENABLE_FTS4_UNICODE61 +#cgo CFLAGS: -DSQLITE_TRACE_SIZE_LIMIT=15 +#cgo CFLAGS: -DSQLITE_ENABLE_COLUMN_METADATA=1 +#cgo CFLAGS: -Wno-deprecated-declarations + +// #ifndef USE_LIBSQLITE3 +// #include "sqlite3-binding.h" +// #else +// #include <sqlite3.h> +// #endif + +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); +} + +#cgo CFLAGS: -I. +#cgo linux LDFLAGS: -ldl +#cgo linux,ppc LDFLAGS: -lpthread +#cgo linux,ppc64 LDFLAGS: -lpthread +#cgo linux,ppc64le LDFLAGS: -lpthread + +#cgo CFLAGS: -D__EXTENSIONS__=1 +#cgo LDFLAGS: -lc + +// #ifndef USE_LIBSQLITE3 +// #include "sqlite3-binding.h" +// #else +// #include <sqlite3.h> +// #endif + +int traceCallbackTrampoline(unsigned int traceEventCode, void *ctx, void *p, void *x); + +// #ifndef USE_LIBSQLITE3 +// #include "sqlite3-binding.h" +// #else +// #include <sqlite3.h> +// #endif + +// #include <windows.h> +// +// void usleep(__int64 usec) +// { +// HANDLE timer; +// LARGE_INTEGER ft; +// +// // Convert to 100 nanosecond interval, negative value indicates relative time +// ft.QuadPart = -(10*usec); +// +// timer = CreateWaitableTimer(NULL, TRUE, NULL); +// SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0); +// WaitForSingleObject(timer, INFINITE); +// CloseHandle(timer); +// } +// +// #cgo CFLAGS: -I. +// #cgo CFLAGS: -fno-stack-check +// #cgo CFLAGS: -fno-stack-protector +// #cgo CFLAGS: -mno-stack-arg-probe +// #cgo windows,386 CFLAGS: -D_USE_32BIT_TIME_T +*/ +import "C" + + + +// SQLiteBackup implement interface of Backup. +type SQLiteBackup struct { + b *C.sqlite3_backup +} + +// Backup make backup from src to dest. +func (destConn *SQLiteConn) Backup(dest string, srcConn *SQLiteConn, src string) (*SQLiteBackup, error) { + destptr := C.CString(dest) + defer C.free(unsafe.Pointer(destptr)) + srcptr := C.CString(src) + defer C.free(unsafe.Pointer(srcptr)) + + if b := C.sqlite3_backup_init(destConn.db, destptr, srcConn.db, srcptr); b != nil { + bb := &SQLiteBackup{b: b} + runtime.SetFinalizer(bb, (*SQLiteBackup).Finish) + return bb, nil + } + return nil, destConn.lastError() +} + +// Step to backs up for one step. Calls the underlying `sqlite3_backup_step` +// function. This function returns a boolean indicating if the backup is done +// and an error signalling any other error. Done is returned if the underlying +// C function returns SQLITE_DONE (Code 101) +func (b *SQLiteBackup) Step(p int) (bool, error) { + ret := C.sqlite3_backup_step(b.b, C.int(p)) + if ret == C.SQLITE_DONE { + return true, nil + } else if ret != 0 && ret != C.SQLITE_LOCKED && ret != C.SQLITE_BUSY { + return false, Error{Code: ErrNo(ret)} + } + return false, nil +} + +// Remaining return whether have the rest for backup. +func (b *SQLiteBackup) Remaining() int { + return int(C.sqlite3_backup_remaining(b.b)) +} + +// PageCount return count of pages. +func (b *SQLiteBackup) PageCount() int { + return int(C.sqlite3_backup_pagecount(b.b)) +} + +// Finish close backup. +func (b *SQLiteBackup) Finish() error { + return b.Close() +} + +// Close close backup. +func (b *SQLiteBackup) Close() error { + ret := C.sqlite3_backup_finish(b.b) + + // sqlite3_backup_finish() never fails, it just returns the + // error code from previous operations, so clean up before + // checking and returning an error + b.b = nil + runtime.SetFinalizer(b, nil) + + if ret != 0 { + return Error{Code: ErrNo(ret)} + } + return nil +} +// Copyright (C) 2019 Yasuhiro Matsumoto <mattn.jp@gmail.com>. +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +/* +//export callbackTrampoline +func callbackTrampoline(ctx *C.sqlite3_context, argc int, argv **C.sqlite3_value) { + args := (*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.sqlite3_value)(nil))]*C.sqlite3_value)(unsafe.Pointer(argv))[:argc:argc] + fi := lookupHandle(C.sqlite3_user_data(ctx)).(*functionInfo) + fi.Call(ctx, args) +} + +//export stepTrampoline +func stepTrampoline(ctx *C.sqlite3_context, argc C.int, argv **C.sqlite3_value) { + args := (*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.sqlite3_value)(nil))]*C.sqlite3_value)(unsafe.Pointer(argv))[:int(argc):int(argc)] + ai := lookupHandle(C.sqlite3_user_data(ctx)).(*aggInfo) + ai.Step(ctx, args) +} + +//export doneTrampoline +func doneTrampoline(ctx *C.sqlite3_context) { + ai := lookupHandle(C.sqlite3_user_data(ctx)).(*aggInfo) + ai.Done(ctx) +} + +//export compareTrampoline +func compareTrampoline(handlePtr unsafe.Pointer, la C.int, a *C.char, lb C.int, b *C.char) C.int { + cmp := lookupHandle(handlePtr).(func(string, string) int) + return C.int(cmp(C.GoStringN(a, la), C.GoStringN(b, lb))) +} + +//export commitHookTrampoline +func commitHookTrampoline(handle unsafe.Pointer) int { + callback := lookupHandle(handle).(func() int) + return callback() +} + +//export rollbackHookTrampoline +func rollbackHookTrampoline(handle unsafe.Pointer) { + callback := lookupHandle(handle).(func()) + callback() +} + +//export updateHookTrampoline +func updateHookTrampoline(handle unsafe.Pointer, op int, db *C.char, table *C.char, rowid int64) { + callback := lookupHandle(handle).(func(int, string, string, int64)) + callback(op, C.GoString(db), C.GoString(table), rowid) +} + +//export authorizerTrampoline +func authorizerTrampoline(handle unsafe.Pointer, op int, arg1 *C.char, arg2 *C.char, arg3 *C.char) int { + callback := lookupHandle(handle).(func(int, string, string, string) int) + return callback(op, C.GoString(arg1), C.GoString(arg2), C.GoString(arg3)) +} + +//export preUpdateHookTrampoline +func preUpdateHookTrampoline(handle unsafe.Pointer, dbHandle uintptr, op int, db *C.char, table *C.char, oldrowid int64, newrowid int64) { + hval := lookupHandleVal(handle) + data := SQLitePreUpdateData{ + Conn: hval.db, + Op: op, + DatabaseName: C.GoString(db), + TableName: C.GoString(table), + OldRowID: oldrowid, + NewRowID: newrowid, + } + callback := hval.val.(func(SQLitePreUpdateData)) + callback(data) +} +*/ + +// Use handles to avoid passing Go pointers to C. +type handleVal struct { + db *SQLiteConn + val any +} + +var handleLock sync.Mutex +var handleVals = make(map[unsafe.Pointer]handleVal) + +func newHandle(db *SQLiteConn, v any) unsafe.Pointer { + handleLock.Lock() + defer handleLock.Unlock() + val := handleVal{db: db, val: v} + var p unsafe.Pointer = C.malloc(C.size_t(1)) + if p == nil { + panic("can't allocate 'cgo-pointer hack index pointer': ptr == nil") + } + handleVals[p] = val + return p +} + +func lookupHandleVal(handle unsafe.Pointer) handleVal { + handleLock.Lock() + defer handleLock.Unlock() + return handleVals[handle] +} + +func lookupHandle(handle unsafe.Pointer) any { + return lookupHandleVal(handle).val +} + +func deleteHandles(db *SQLiteConn) { + handleLock.Lock() + defer handleLock.Unlock() + for handle, val := range handleVals { + if val.db == db { + delete(handleVals, handle) + C.free(handle) + } + } +} + +// 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 { + f callbackArgConverter + typ reflect.Type +} + +func (c callbackArgCast) Run(v *C.sqlite3_value) (reflect.Value, error) { + val, err := c.f(v) + if err != nil { + return reflect.Value{}, err + } + if !val.Type().ConvertibleTo(c.typ) { + return reflect.Value{}, fmt.Errorf("cannot convert %s to %s", val.Type(), c.typ) + } + return val.Convert(c.typ), nil +} + +func callbackArgInt64(v *C.sqlite3_value) (reflect.Value, error) { + if C.sqlite3_value_type(v) != C.SQLITE_INTEGER { + return reflect.Value{}, fmt.Errorf("argument must be an INTEGER") + } + return reflect.ValueOf(int64(C.sqlite3_value_int64(v))), nil +} + +func callbackArgBool(v *C.sqlite3_value) (reflect.Value, error) { + if C.sqlite3_value_type(v) != C.SQLITE_INTEGER { + return reflect.Value{}, fmt.Errorf("argument must be an INTEGER") + } + i := int64(C.sqlite3_value_int64(v)) + val := false + if i != 0 { + val = true + } + return reflect.ValueOf(val), nil +} + +func callbackArgFloat64(v *C.sqlite3_value) (reflect.Value, error) { + if C.sqlite3_value_type(v) != C.SQLITE_FLOAT { + return reflect.Value{}, fmt.Errorf("argument must be a FLOAT") + } + return reflect.ValueOf(float64(C.sqlite3_value_double(v))), nil +} + +func callbackArgBytes(v *C.sqlite3_value) (reflect.Value, error) { + switch C.sqlite3_value_type(v) { + case C.SQLITE_BLOB: + l := C.sqlite3_value_bytes(v) + p := C.sqlite3_value_blob(v) + return reflect.ValueOf(C.GoBytes(p, l)), nil + case C.SQLITE_TEXT: + l := C.sqlite3_value_bytes(v) + c := unsafe.Pointer(C.sqlite3_value_text(v)) + return reflect.ValueOf(C.GoBytes(c, l)), nil + default: + return reflect.Value{}, fmt.Errorf("argument must be BLOB or TEXT") + } +} + +func callbackArgString(v *C.sqlite3_value) (reflect.Value, error) { + switch C.sqlite3_value_type(v) { + case C.SQLITE_BLOB: + l := C.sqlite3_value_bytes(v) + p := (*C.char)(C.sqlite3_value_blob(v)) + return reflect.ValueOf(C.GoStringN(p, l)), nil + case C.SQLITE_TEXT: + c := (*C.char)(unsafe.Pointer(C.sqlite3_value_text(v))) + return reflect.ValueOf(C.GoString(c)), nil + default: + return reflect.Value{}, fmt.Errorf("argument must be BLOB or TEXT") + } +} + +func callbackArgGeneric(v *C.sqlite3_value) (reflect.Value, error) { + switch C.sqlite3_value_type(v) { + case C.SQLITE_INTEGER: + return callbackArgInt64(v) + case C.SQLITE_FLOAT: + return callbackArgFloat64(v) + case C.SQLITE_TEXT: + return callbackArgString(v) + case C.SQLITE_BLOB: + return callbackArgBytes(v) + case C.SQLITE_NULL: + // Interpret NULL as a nil byte slice. + var ret []byte + return reflect.ValueOf(ret), nil + default: + panic("unreachable") + } +} + +func callbackArg(typ reflect.Type) (callbackArgConverter, error) { + switch typ.Kind() { + case reflect.Interface: + if typ.NumMethod() != 0 { + return nil, errors.New("the only supported interface type is any") + } + return callbackArgGeneric, nil + case reflect.Slice: + if typ.Elem().Kind() != reflect.Uint8 { + return nil, errors.New("the only supported slice type is []byte") + } + return callbackArgBytes, nil + case reflect.String: + return callbackArgString, nil + case reflect.Bool: + return callbackArgBool, nil + case reflect.Int64: + return callbackArgInt64, nil + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Int, reflect.Uint: + c := callbackArgCast{callbackArgInt64, typ} + return c.Run, nil + case reflect.Float64: + return callbackArgFloat64, nil + case reflect.Float32: + c := callbackArgCast{callbackArgFloat64, typ} + return c.Run, nil + default: + return nil, fmt.Errorf("don't know how to convert to %s", typ) + } +} + +func callbackConvertArgs(argv []*C.sqlite3_value, converters []callbackArgConverter, variadic callbackArgConverter) ([]reflect.Value, error) { + var args []reflect.Value + + if len(argv) < len(converters) { + return nil, fmt.Errorf("function requires at least %d arguments", len(converters)) + } + + for i, arg := range argv[:len(converters)] { + v, err := converters[i](arg) + if err != nil { + return nil, err + } + args = append(args, v) + } + + if variadic != nil { + for _, arg := range argv[len(converters):] { + v, err := variadic(arg) + if err != nil { + return nil, err + } + args = append(args, v) + } + } + return args, nil +} + +type callbackRetConverter func(*C.sqlite3_context, reflect.Value) error + +func callbackRetInteger(ctx *C.sqlite3_context, v reflect.Value) error { + switch v.Type().Kind() { + case reflect.Int64: + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Int, reflect.Uint: + v = v.Convert(reflect.TypeOf(int64(0))) + case reflect.Bool: + b := v.Interface().(bool) + if b { + v = reflect.ValueOf(int64(1)) + } else { + v = reflect.ValueOf(int64(0)) + } + default: + return fmt.Errorf("cannot convert %s to INTEGER", v.Type()) + } + + C.sqlite3_result_int64(ctx, C.sqlite3_int64(v.Interface().(int64))) + return nil +} + +func callbackRetFloat(ctx *C.sqlite3_context, v reflect.Value) error { + switch v.Type().Kind() { + case reflect.Float64: + case reflect.Float32: + v = v.Convert(reflect.TypeOf(float64(0))) + default: + return fmt.Errorf("cannot convert %s to FLOAT", v.Type()) + } + + C.sqlite3_result_double(ctx, C.double(v.Interface().(float64))) + return nil +} + +func callbackRetBlob(ctx *C.sqlite3_context, v reflect.Value) error { + if v.Type().Kind() != reflect.Slice || v.Type().Elem().Kind() != reflect.Uint8 { + return fmt.Errorf("cannot convert %s to BLOB", v.Type()) + } + i := v.Interface() + if i == nil || len(i.([]byte)) == 0 { + C.sqlite3_result_null(ctx) + } else { + bs := i.([]byte) + C._sqlite3_result_blob(ctx, unsafe.Pointer(&bs[0]), C.int(len(bs))) + } + return nil +} + +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))) + return nil +} + +func callbackRetNil(ctx *C.sqlite3_context, v reflect.Value) error { + return nil +} + +func callbackRetGeneric(ctx *C.sqlite3_context, v reflect.Value) error { + if v.IsNil() { + C.sqlite3_result_null(ctx) + return nil + } + + cb, err := callbackRet(v.Elem().Type()) + if err != nil { + return err + } + + return cb(ctx, v.Elem()) +} + +func callbackRet(typ reflect.Type) (callbackRetConverter, error) { + switch typ.Kind() { + case reflect.Interface: + errorInterface := reflect.TypeOf((*error)(nil)).Elem() + if typ.Implements(errorInterface) { + return callbackRetNil, nil + } + + if typ.NumMethod() == 0 { + return callbackRetGeneric, nil + } + + fallthrough + case reflect.Slice: + if typ.Elem().Kind() != reflect.Uint8 { + return nil, errors.New("the only supported slice type is []byte") + } + return callbackRetBlob, nil + case reflect.String: + return callbackRetText, nil + case reflect.Bool, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Int, reflect.Uint: + return callbackRetInteger, nil + case reflect.Float32, reflect.Float64: + return callbackRetFloat, nil + default: + return nil, fmt.Errorf("don't know how to convert to %s", typ) + } +} + +func callbackError(ctx *C.sqlite3_context, err error) { + cstr := C.CString(err.Error()) + defer C.free(unsafe.Pointer(cstr)) + C.sqlite3_result_error(ctx, cstr, C.int(-1)) +} + +// 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 { + return func(*C.sqlite3_value) (reflect.Value, error) { + return v, err + } +} +// Extracted from Go database/sql source code + +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Type conversions for Scan. + +var errNilPtr = errors.New("destination pointer is nil") // embedded in descriptive error + +// convertAssign copies to dest the value in src, converting it if possible. +// An error is returned if the copy would result in loss of information. +// dest should be a pointer type. +func convertAssign(dest, src any) error { + // Common cases, without reflect. + switch s := src.(type) { + case string: + switch d := dest.(type) { + case *string: + if d == nil { + return errNilPtr + } + *d = s + return nil + case *[]byte: + if d == nil { + return errNilPtr + } + *d = []byte(s) + return nil + case *sql.RawBytes: + if d == nil { + return errNilPtr + } + *d = append((*d)[:0], s...) + return nil + } + case []byte: + switch d := dest.(type) { + case *string: + if d == nil { + return errNilPtr + } + *d = string(s) + return nil + case *any: + if d == nil { + return errNilPtr + } + *d = cloneBytes(s) + return nil + case *[]byte: + if d == nil { + return errNilPtr + } + *d = cloneBytes(s) + return nil + case *sql.RawBytes: + if d == nil { + return errNilPtr + } + *d = s + return nil + } + case time.Time: + switch d := dest.(type) { + case *time.Time: + *d = s + return nil + case *string: + *d = s.Format(time.RFC3339Nano) + return nil + case *[]byte: + if d == nil { + return errNilPtr + } + *d = []byte(s.Format(time.RFC3339Nano)) + return nil + case *sql.RawBytes: + if d == nil { + return errNilPtr + } + *d = s.AppendFormat((*d)[:0], time.RFC3339Nano) + return nil + } + case nil: + switch d := dest.(type) { + case *any: + if d == nil { + return errNilPtr + } + *d = nil + return nil + case *[]byte: + if d == nil { + return errNilPtr + } + *d = nil + return nil + case *sql.RawBytes: + if d == nil { + return errNilPtr + } + *d = nil + return nil + } + } + + var sv reflect.Value + + switch d := dest.(type) { + case *string: + sv = reflect.ValueOf(src) + switch sv.Kind() { + case reflect.Bool, + reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, + reflect.Float32, reflect.Float64: + *d = asString(src) + return nil + } + case *[]byte: + sv = reflect.ValueOf(src) + if b, ok := asBytes(nil, sv); ok { + *d = b + return nil + } + case *sql.RawBytes: + sv = reflect.ValueOf(src) + if b, ok := asBytes([]byte(*d)[:0], sv); ok { + *d = sql.RawBytes(b) + return nil + } + case *bool: + bv, err := driver.Bool.ConvertValue(src) + if err == nil { + *d = bv.(bool) + } + return err + case *any: + *d = src + return nil + } + + if scanner, ok := dest.(sql.Scanner); ok { + return scanner.Scan(src) + } + + dpv := reflect.ValueOf(dest) + if dpv.Kind() != reflect.Ptr { + return errors.New("destination not a pointer") + } + if dpv.IsNil() { + return errNilPtr + } + + if !sv.IsValid() { + sv = reflect.ValueOf(src) + } + + dv := reflect.Indirect(dpv) + if sv.IsValid() && sv.Type().AssignableTo(dv.Type()) { + switch b := src.(type) { + case []byte: + dv.Set(reflect.ValueOf(cloneBytes(b))) + default: + dv.Set(sv) + } + return nil + } + + if dv.Kind() == sv.Kind() && sv.Type().ConvertibleTo(dv.Type()) { + dv.Set(sv.Convert(dv.Type())) + return nil + } + + // The following conversions use a string value as an intermediate representation + // to convert between various numeric types. + // + // This also allows scanning into user defined types such as "type Int int64". + // For symmetry, also check for string destination types. + switch dv.Kind() { + case reflect.Ptr: + if src == nil { + dv.Set(reflect.Zero(dv.Type())) + return nil + } + dv.Set(reflect.New(dv.Type().Elem())) + return convertAssign(dv.Interface(), src) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + s := asString(src) + i64, err := strconv.ParseInt(s, 10, dv.Type().Bits()) + if err != nil { + err = strconvErr(err) + return fmt.Errorf("converting driver.Value type %T (%q) to a %s: %v", src, s, dv.Kind(), err) + } + dv.SetInt(i64) + return nil + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + s := asString(src) + u64, err := strconv.ParseUint(s, 10, dv.Type().Bits()) + if err != nil { + err = strconvErr(err) + return fmt.Errorf("converting driver.Value type %T (%q) to a %s: %v", src, s, dv.Kind(), err) + } + dv.SetUint(u64) + return nil + case reflect.Float32, reflect.Float64: + s := asString(src) + f64, err := strconv.ParseFloat(s, dv.Type().Bits()) + if err != nil { + err = strconvErr(err) + return fmt.Errorf("converting driver.Value type %T (%q) to a %s: %v", src, s, dv.Kind(), err) + } + dv.SetFloat(f64) + return nil + case reflect.String: + switch v := src.(type) { + case string: + dv.SetString(v) + return nil + case []byte: + dv.SetString(string(v)) + return nil + } + } + + return fmt.Errorf("unsupported Scan, storing driver.Value type %T into type %T", src, dest) +} + +func strconvErr(err error) error { + if ne, ok := err.(*strconv.NumError); ok { + return ne.Err + } + return err +} + +func cloneBytes(b []byte) []byte { + if b == nil { + return nil + } + c := make([]byte, len(b)) + copy(c, b) + return c +} + +func asString(src any) string { + switch v := src.(type) { + case string: + return v + case []byte: + return string(v) + } + rv := reflect.ValueOf(src) + switch rv.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return strconv.FormatInt(rv.Int(), 10) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return strconv.FormatUint(rv.Uint(), 10) + case reflect.Float64: + return strconv.FormatFloat(rv.Float(), 'g', -1, 64) + case reflect.Float32: + return strconv.FormatFloat(rv.Float(), 'g', -1, 32) + case reflect.Bool: + return strconv.FormatBool(rv.Bool()) + } + return fmt.Sprintf("%v", src) +} + +func asBytes(buf []byte, rv reflect.Value) (b []byte, ok bool) { + switch rv.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return strconv.AppendInt(buf, rv.Int(), 10), true + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return strconv.AppendUint(buf, rv.Uint(), 10), true + case reflect.Float32: + return strconv.AppendFloat(buf, rv.Float(), 'g', -1, 32), true + case reflect.Float64: + return strconv.AppendFloat(buf, rv.Float(), 'g', -1, 64), true + case reflect.Bool: + return strconv.AppendBool(buf, rv.Bool()), true + case reflect.String: + s := rv.String() + return append(buf, s...), true + } + return +} +/* +Package sqlite3 provides interface to SQLite3 databases. + +This works as a driver for database/sql. + +Installation + + go get github.com/mattn/go-sqlite3 + +# Supported Types + +Currently, go-sqlite3 supports the following data types. + + +------------------------------+ + |go | sqlite3 | + |----------|-------------------| + |nil | null | + |int | integer | + |int64 | integer | + |float64 | float | + |bool | integer | + |[]byte | blob | + |string | text | + |time.Time | timestamp/datetime| + +------------------------------+ + +# SQLite3 Extension + +You can write your own extension module for sqlite3. For example, below is an +extension for a Regexp matcher operation. + + #include <pcre.h> + #include <string.h> + #include <stdio.h> + #include <sqlite3ext.h> + + SQLITE_EXTENSION_INIT1 + static void regexp_func(sqlite3_context *context, int argc, sqlite3_value **argv) { + if (argc >= 2) { + const char *target = (const char *)sqlite3_value_text(argv[1]); + const char *pattern = (const char *)sqlite3_value_text(argv[0]); + const char* errstr = NULL; + int erroff = 0; + int vec[500]; + int n, rc; + pcre* re = pcre_compile(pattern, 0, &errstr, &erroff, NULL); + rc = pcre_exec(re, NULL, target, strlen(target), 0, 0, vec, 500); + if (rc <= 0) { + sqlite3_result_error(context, errstr, 0); + return; + } + sqlite3_result_int(context, 1); + } + } + + #ifdef _WIN32 + __declspec(dllexport) + #endif + int sqlite3_extension_init(sqlite3 *db, char **errmsg, + const sqlite3_api_routines *api) { + SQLITE_EXTENSION_INIT2(api); + return sqlite3_create_function(db, "regexp", 2, SQLITE_UTF8, + (void*)db, regexp_func, NULL, NULL); + } + +It needs to be built as a so/dll shared library. And you need to register +the extension module like below. + + sql.Register("sqlite3_with_extensions", + &sqlite3.SQLiteDriver{ + Extensions: []string{ + "sqlite3_mod_regexp", + }, + }) + +Then, you can use this extension. + + rows, err := db.Query("select text from mytable where name regexp '^golang'") + +# Connection Hook + +You can hook and inject your code when the connection is established by setting +ConnectHook to get the SQLiteConn. + + sql.Register("sqlite3_with_hook_example", + &sqlite3.SQLiteDriver{ + ConnectHook: func(conn *sqlite3.SQLiteConn) error { + sqlite3conn = append(sqlite3conn, conn) + return nil + }, + }) + +You can also use database/sql.Conn.Raw (Go >= 1.13): + + conn, err := db.Conn(context.Background()) + // if err != nil { ... } + defer conn.Close() + err = conn.Raw(func (driverConn any) error { + sqliteConn := driverConn.(*sqlite3.SQLiteConn) + // ... use sqliteConn + }) + // if err != nil { ... } + +# Go SQlite3 Extensions + +If you want to register Go functions as SQLite extension functions +you can make a custom driver by calling RegisterFunction from +ConnectHook. + + regex = func(re, s string) (bool, error) { + return regexp.MatchString(re, s) + } + sql.Register("sqlite3_extended", + &sqlite3.SQLiteDriver{ + ConnectHook: func(conn *sqlite3.SQLiteConn) error { + return conn.RegisterFunc("regexp", regex, true) + }, + }) + +You can then use the custom driver by passing its name to sql.Open. + + var i int + conn, err := sql.Open("sqlite3_extended", "./foo.db") + if err != nil { + panic(err) + } + err = db.QueryRow(`SELECT regexp("foo.*", "seafood")`).Scan(&i) + if err != nil { + panic(err) + } + +See the documentation of RegisterFunc for more details. +*/ +// Copyright (C) 2019 Yasuhiro Matsumoto <mattn.jp@gmail.com>. +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +// ErrNo inherit errno. +type ErrNo int + +// ErrNoMask is mask code. +const ErrNoMask C.int = 0xff + +// ErrNoExtended is extended errno. +type ErrNoExtended int + +// Error implement sqlite error code. +type Error struct { + Code ErrNo /* The error code returned by SQLite */ + ExtendedCode ErrNoExtended /* The extended error code returned by SQLite */ + SystemErrno syscall.Errno /* The system errno returned by the OS through SQLite, if applicable */ + err string /* The error string returned by sqlite3_errmsg(), + this usually contains more specific details. */ +} + +// result codes from http://www.sqlite.org/c3ref/c_abort.html +var ( + ErrError = ErrNo(1) /* SQL error or missing database */ + ErrInternal = ErrNo(2) /* Internal logic error in SQLite */ + ErrPerm = ErrNo(3) /* Access permission denied */ + ErrAbort = ErrNo(4) /* Callback routine requested an abort */ + ErrBusy = ErrNo(5) /* The database file is locked */ + ErrLocked = ErrNo(6) /* A table in the database is locked */ + ErrNomem = ErrNo(7) /* A malloc() failed */ + ErrReadonly = ErrNo(8) /* Attempt to write a readonly database */ + ErrInterrupt = ErrNo(9) /* Operation terminated by sqlite3_interrupt() */ + ErrIoErr = ErrNo(10) /* Some kind of disk I/O error occurred */ + ErrCorrupt = ErrNo(11) /* The database disk image is malformed */ + ErrNotFound = ErrNo(12) /* Unknown opcode in sqlite3_file_control() */ + ErrFull = ErrNo(13) /* Insertion failed because database is full */ + ErrCantOpen = ErrNo(14) /* Unable to open the database file */ + ErrProtocol = ErrNo(15) /* Database lock protocol error */ + ErrEmpty = ErrNo(16) /* Database is empty */ + ErrSchema = ErrNo(17) /* The database schema changed */ + ErrTooBig = ErrNo(18) /* String or BLOB exceeds size limit */ + ErrConstraint = ErrNo(19) /* Abort due to constraint violation */ + ErrMismatch = ErrNo(20) /* Data type mismatch */ + ErrMisuse = ErrNo(21) /* Library used incorrectly */ + ErrNoLFS = ErrNo(22) /* Uses OS features not supported on host */ + ErrAuth = ErrNo(23) /* Authorization denied */ + ErrFormat = ErrNo(24) /* Auxiliary database format error */ + ErrRange = ErrNo(25) /* 2nd parameter to sqlite3_bind out of range */ + ErrNotADB = ErrNo(26) /* File opened that is not a database file */ + ErrNotice = ErrNo(27) /* Notifications from sqlite3_log() */ + ErrWarning = ErrNo(28) /* Warnings from sqlite3_log() */ +) + +// Error return error message from errno. +func (err ErrNo) Error() string { + return Error{Code: err}.Error() +} + +// Extend return extended errno. +func (err ErrNo) Extend(by int) ErrNoExtended { + return ErrNoExtended(int(err) | (by << 8)) +} + +// Error return error message that is extended code. +func (err ErrNoExtended) Error() string { + return Error{Code: ErrNo(C.int(err) & ErrNoMask), ExtendedCode: err}.Error() +} + +func (err Error) Error() string { + var str string + if err.err != "" { + str = err.err + } else { + str = C.GoString(C.sqlite3_errstr(C.int(err.Code))) + } + if err.SystemErrno != 0 { + str += ": " + err.SystemErrno.Error() + } + return str +} + +// result codes from http://www.sqlite.org/c3ref/c_abort_rollback.html +var ( + ErrIoErrRead = ErrIoErr.Extend(1) + ErrIoErrShortRead = ErrIoErr.Extend(2) + ErrIoErrWrite = ErrIoErr.Extend(3) + ErrIoErrFsync = ErrIoErr.Extend(4) + ErrIoErrDirFsync = ErrIoErr.Extend(5) + ErrIoErrTruncate = ErrIoErr.Extend(6) + ErrIoErrFstat = ErrIoErr.Extend(7) + ErrIoErrUnlock = ErrIoErr.Extend(8) + ErrIoErrRDlock = ErrIoErr.Extend(9) + ErrIoErrDelete = ErrIoErr.Extend(10) + ErrIoErrBlocked = ErrIoErr.Extend(11) + ErrIoErrNoMem = ErrIoErr.Extend(12) + ErrIoErrAccess = ErrIoErr.Extend(13) + ErrIoErrCheckReservedLock = ErrIoErr.Extend(14) + ErrIoErrLock = ErrIoErr.Extend(15) + ErrIoErrClose = ErrIoErr.Extend(16) + ErrIoErrDirClose = ErrIoErr.Extend(17) + ErrIoErrSHMOpen = ErrIoErr.Extend(18) + ErrIoErrSHMSize = ErrIoErr.Extend(19) + ErrIoErrSHMLock = ErrIoErr.Extend(20) + ErrIoErrSHMMap = ErrIoErr.Extend(21) + ErrIoErrSeek = ErrIoErr.Extend(22) + ErrIoErrDeleteNoent = ErrIoErr.Extend(23) + ErrIoErrMMap = ErrIoErr.Extend(24) + ErrIoErrGetTempPath = ErrIoErr.Extend(25) + ErrIoErrConvPath = ErrIoErr.Extend(26) + ErrLockedSharedCache = ErrLocked.Extend(1) + ErrBusyRecovery = ErrBusy.Extend(1) + ErrBusySnapshot = ErrBusy.Extend(2) + ErrCantOpenNoTempDir = ErrCantOpen.Extend(1) + ErrCantOpenIsDir = ErrCantOpen.Extend(2) + ErrCantOpenFullPath = ErrCantOpen.Extend(3) + ErrCantOpenConvPath = ErrCantOpen.Extend(4) + ErrCorruptVTab = ErrCorrupt.Extend(1) + ErrReadonlyRecovery = ErrReadonly.Extend(1) + ErrReadonlyCantLock = ErrReadonly.Extend(2) + ErrReadonlyRollback = ErrReadonly.Extend(3) + ErrReadonlyDbMoved = ErrReadonly.Extend(4) + ErrAbortRollback = ErrAbort.Extend(2) + ErrConstraintCheck = ErrConstraint.Extend(1) + ErrConstraintCommitHook = ErrConstraint.Extend(2) + ErrConstraintForeignKey = ErrConstraint.Extend(3) + ErrConstraintFunction = ErrConstraint.Extend(4) + ErrConstraintNotNull = ErrConstraint.Extend(5) + ErrConstraintPrimaryKey = ErrConstraint.Extend(6) + ErrConstraintTrigger = ErrConstraint.Extend(7) + ErrConstraintUnique = ErrConstraint.Extend(8) + ErrConstraintVTab = ErrConstraint.Extend(9) + ErrConstraintRowID = ErrConstraint.Extend(10) + ErrNoticeRecoverWAL = ErrNotice.Extend(1) + ErrNoticeRecoverRollback = ErrNotice.Extend(2) + ErrWarningAutoIndex = ErrWarning.Extend(1) +) +// Copyright (C) 2019 Yasuhiro Matsumoto <mattn.jp@gmail.com>. +// Copyright (C) 2018 G.J.R. Timmer <gjr.timmer@gmail.com>. +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +// //go:build cgo // +build cgo + +// 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 +// column, the formats are tried in order. +var SQLiteTimestampFormats = []string{ + // By default, store timestamps with whatever timezone they come with. + // When parsed, they will be returned with the same timezone. + "2006-01-02 15:04:05.999999999-07:00", + "2006-01-02T15:04:05.999999999-07:00", + "2006-01-02 15:04:05.999999999", + "2006-01-02T15:04:05.999999999", + "2006-01-02 15:04:05", + "2006-01-02T15:04:05", + "2006-01-02 15:04", + "2006-01-02T15:04", + "2006-01-02", +} + +const ( + columnDate string = "date" + columnDatetime string = "datetime" + columnTimestamp string = "timestamp" +) + +// This variable can be replaced with -ldflags like below: +// go build -ldflags="-X 'github.com/mattn/go-sqlite3.driverName=my-sqlite3'" +var driverName = "sqlite3" + +/* +func init() { + sql.Register("sqlite3", &SQLiteDriver{}) +} +*/ + +func init() { + if driverName != "" { + sql.Register(driverName, &SQLiteDriver{}) + } +} + +// Version returns SQLite library version information. +func Version() (libVersion string, libVersionNumber int, sourceID string) { + libVersion = C.GoString(C.sqlite3_libversion()) + libVersionNumber = int(C.sqlite3_libversion_number()) + sourceID = C.GoString(C.sqlite3_sourceid()) + return libVersion, libVersionNumber, sourceID +} + +const ( + // used by authorizer and pre_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 +// See: https://www.sqlite.org/c3ref/c_fcntl_begin_atomic_write.html +const ( + SQLITE_FCNTL_LOCKSTATE = int(1) + SQLITE_FCNTL_GET_LOCKPROXYFILE = int(2) + SQLITE_FCNTL_SET_LOCKPROXYFILE = int(3) + SQLITE_FCNTL_LAST_ERRNO = int(4) + SQLITE_FCNTL_SIZE_HINT = int(5) + SQLITE_FCNTL_CHUNK_SIZE = int(6) + SQLITE_FCNTL_FILE_POINTER = int(7) + SQLITE_FCNTL_SYNC_OMITTED = int(8) + SQLITE_FCNTL_WIN32_AV_RETRY = int(9) + SQLITE_FCNTL_PERSIST_WAL = int(10) + SQLITE_FCNTL_OVERWRITE = int(11) + SQLITE_FCNTL_VFSNAME = int(12) + SQLITE_FCNTL_POWERSAFE_OVERWRITE = int(13) + SQLITE_FCNTL_PRAGMA = int(14) + SQLITE_FCNTL_BUSYHANDLER = int(15) + SQLITE_FCNTL_TEMPFILENAME = int(16) + SQLITE_FCNTL_MMAP_SIZE = int(18) + SQLITE_FCNTL_TRACE = int(19) + SQLITE_FCNTL_HAS_MOVED = int(20) + SQLITE_FCNTL_SYNC = int(21) + SQLITE_FCNTL_COMMIT_PHASETWO = int(22) + SQLITE_FCNTL_WIN32_SET_HANDLE = int(23) + SQLITE_FCNTL_WAL_BLOCK = int(24) + SQLITE_FCNTL_ZIPVFS = int(25) + SQLITE_FCNTL_RBU = int(26) + SQLITE_FCNTL_VFS_POINTER = int(27) + SQLITE_FCNTL_JOURNAL_POINTER = int(28) + SQLITE_FCNTL_WIN32_GET_HANDLE = int(29) + SQLITE_FCNTL_PDB = int(30) + SQLITE_FCNTL_BEGIN_ATOMIC_WRITE = int(31) + SQLITE_FCNTL_COMMIT_ATOMIC_WRITE = int(32) + SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE = int(33) + SQLITE_FCNTL_LOCK_TIMEOUT = int(34) + SQLITE_FCNTL_DATA_VERSION = int(35) + SQLITE_FCNTL_SIZE_LIMIT = int(36) + SQLITE_FCNTL_CKPT_DONE = int(37) + SQLITE_FCNTL_RESERVE_BYTES = int(38) + SQLITE_FCNTL_CKPT_START = int(39) + SQLITE_FCNTL_EXTERNAL_READER = int(40) + SQLITE_FCNTL_CKSM_FILE = int(41) +) + +// SQLiteDriver implements driver.Driver. +type SQLiteDriver struct { + Extensions []string + ConnectHook func(*SQLiteConn) error +} + +// SQLiteConn implements driver.Conn. +type SQLiteConn struct { + mu sync.Mutex + db *C.sqlite3 + loc *time.Location + txlock string + funcs []*functionInfo + aggregators []*aggInfo +} + +// SQLiteTx implements driver.Tx. +type SQLiteTx struct { + c *SQLiteConn +} + +// SQLiteStmt implements driver.Stmt. +type SQLiteStmt struct { + mu sync.Mutex + c *SQLiteConn + s *C.sqlite3_stmt + t string + closed bool + cls bool +} + +// SQLiteResult implements sql.Result. +type SQLiteResult struct { + id int64 + changes int64 +} + +// SQLiteRows implements driver.Rows. +type SQLiteRows struct { + s *SQLiteStmt + nc int + cols []string + decltype []string + cls bool + closed bool + ctx context.Context // no better alternative to pass context into Next() method +} + +type functionInfo struct { + f reflect.Value + argConverters []callbackArgConverter + variadicConverter callbackArgConverter + retConverter callbackRetConverter +} + +func (fi *functionInfo) Call(ctx *C.sqlite3_context, argv []*C.sqlite3_value) { + args, err := callbackConvertArgs(argv, fi.argConverters, fi.variadicConverter) + if err != nil { + callbackError(ctx, err) + return + } + + ret := fi.f.Call(args) + + if len(ret) == 2 && ret[1].Interface() != nil { + callbackError(ctx, ret[1].Interface().(error)) + return + } + + err = fi.retConverter(ctx, ret[0]) + if err != nil { + callbackError(ctx, err) + return + } +} + +type aggInfo struct { + constructor reflect.Value + + // Active aggregator objects for aggregations in flight. The + // aggregators are indexed by a counter stored in the aggregation + // user data space provided by sqlite. + active map[int64]reflect.Value + next int64 + + stepArgConverters []callbackArgConverter + stepVariadicConverter callbackArgConverter + + doneRetConverter callbackRetConverter +} + +func (ai *aggInfo) agg(ctx *C.sqlite3_context) (int64, reflect.Value, error) { + aggIdx := (*int64)(C.sqlite3_aggregate_context(ctx, C.int(8))) + if *aggIdx == 0 { + *aggIdx = ai.next + ret := ai.constructor.Call(nil) + if len(ret) == 2 && ret[1].Interface() != nil { + return 0, reflect.Value{}, ret[1].Interface().(error) + } + if ret[0].IsNil() { + return 0, reflect.Value{}, errors.New("aggregator constructor returned nil state") + } + ai.next++ + ai.active[*aggIdx] = ret[0] + } + return *aggIdx, ai.active[*aggIdx], nil +} + +func (ai *aggInfo) Step(ctx *C.sqlite3_context, argv []*C.sqlite3_value) { + _, agg, err := ai.agg(ctx) + if err != nil { + callbackError(ctx, err) + return + } + + args, err := callbackConvertArgs(argv, ai.stepArgConverters, ai.stepVariadicConverter) + if err != nil { + callbackError(ctx, err) + return + } + + ret := agg.MethodByName("Step").Call(args) + if len(ret) == 1 && ret[0].Interface() != nil { + callbackError(ctx, ret[0].Interface().(error)) + return + } +} + +func (ai *aggInfo) Done(ctx *C.sqlite3_context) { + idx, agg, err := ai.agg(ctx) + if err != nil { + callbackError(ctx, err) + return + } + defer func() { delete(ai.active, idx) }() + + ret := agg.MethodByName("Done").Call(nil) + if len(ret) == 2 && ret[1].Interface() != nil { + callbackError(ctx, ret[1].Interface().(error)) + return + } + + err = ai.doneRetConverter(ctx, ret[0]) + if err != nil { + callbackError(ctx, err) + return + } +} + +// Commit transaction. +func (tx *SQLiteTx) Commit() error { + _, err := tx.c.exec(context.Background(), "COMMIT", nil) + if err != nil { + // sqlite3 may leave the transaction open in this scenario. + // However, database/sql considers the transaction complete once we + // return from Commit() - we must clean up to honour its semantics. + // We don't know if the ROLLBACK is strictly necessary, but according + // to sqlite's docs, there is no harm in calling ROLLBACK unnecessarily. + tx.c.exec(context.Background(), "ROLLBACK", nil) + } + return err +} + +// Rollback transaction. +func (tx *SQLiteTx) Rollback() error { + _, err := tx.c.exec(context.Background(), "ROLLBACK", nil) + return err +} + +// RegisterCollation makes a Go function available as a collation. +// +// cmp receives two UTF-8 strings, a and b. The result should be 0 if +// a==b, -1 if a < b, and +1 if a > b. +// +// cmp must always return the same result given the same +// inputs. Additionally, it must have the following properties for all +// strings A, B and C: if A==B then B==A; if A==B and B==C then A==C; +// if A<B then B>A; if A<B and B<C then A<C. +// +// If cmp does not obey these constraints, sqlite3's behavior is +// undefined when the collation is used. +func (c *SQLiteConn) RegisterCollation(name string, cmp func(string, string) int) error { + handle := newHandle(c, cmp) + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + rv := C.sqlite3_create_collation(c.db, cname, C.SQLITE_UTF8, handle, (*[0]byte)(unsafe.Pointer(C.compareTrampoline))) + if rv != C.SQLITE_OK { + return c.lastError() + } + 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) + } else { + C.sqlite3_commit_hook(c.db, (*[0]byte)(C.commitHookTrampoline), newHandle(c, callback)) + } +} + +// 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) + } else { + C.sqlite3_rollback_hook(c.db, (*[0]byte)(C.rollbackHookTrampoline), newHandle(c, callback)) + } +} + +// 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) + } else { + C.sqlite3_update_hook(c.db, (*[0]byte)(C.updateHookTrampoline), newHandle(c, callback)) + } +} + +// 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 +// numeric type except complex, bool, []byte, string and any. +// any arguments are given the direct translation of the SQLite data type: +// int64 for INTEGER, float64 for FLOAT, []byte for BLOB, string for TEXT. +// +// The function can additionally be variadic, as long as the type of +// the variadic argument is one of the above. +// +// If pure is true. SQLite will assume that the function's return +// value depends only on its inputs, and make more aggressive +// optimizations in its queries. +// +// See _example/go_custom_funcs for a detailed example. +func (c *SQLiteConn) RegisterFunc(name string, impl any, pure bool) error { + var fi functionInfo + fi.f = reflect.ValueOf(impl) + t := fi.f.Type() + if t.Kind() != reflect.Func { + return errors.New("Non-function passed to RegisterFunc") + } + if t.NumOut() != 1 && t.NumOut() != 2 { + return errors.New("SQLite functions must return 1 or 2 values") + } + if t.NumOut() == 2 && !t.Out(1).Implements(reflect.TypeOf((*error)(nil)).Elem()) { + return errors.New("Second return value of SQLite function must be error") + } + + numArgs := t.NumIn() + if t.IsVariadic() { + numArgs-- + } + + for i := 0; i < numArgs; i++ { + conv, err := callbackArg(t.In(i)) + if err != nil { + return err + } + fi.argConverters = append(fi.argConverters, conv) + } + + if t.IsVariadic() { + conv, err := callbackArg(t.In(numArgs).Elem()) + if err != nil { + return err + } + fi.variadicConverter = conv + // Pass -1 to sqlite so that it allows any number of + // arguments. The call helper verifies that the minimum number + // of arguments is present for variadic functions. + numArgs = -1 + } + + conv, err := callbackRet(t.Out(0)) + if err != nil { + return err + } + fi.retConverter = conv + + // fi must outlast the database connection, or we'll have dangling pointers. + c.funcs = append(c.funcs, &fi) + + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + opts := C.SQLITE_UTF8 + if pure { + opts |= C.SQLITE_DETERMINISTIC + } + rv := sqlite3CreateFunction(c.db, cname, C.int(numArgs), C.int(opts), newHandle(c, &fi), 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 +// type that has 2 methods: func Step(values) accumulates one row of +// data into the accumulator, and func Done() ret finalizes and +// returns the aggregate value. "values" and "ret" may be any type +// supported by RegisterFunc. +// +// RegisterAggregator takes as implementation a constructor function +// that constructs an instance of the aggregator type each time an +// aggregation begins. The constructor must return a pointer to a +// type, or an interface that implements Step() and Done(). +// +// The constructor function and the Step/Done methods may optionally +// return an error in addition to their other return values. +// +// See _example/go_custom_funcs for a detailed example. +func (c *SQLiteConn) RegisterAggregator(name string, impl any, pure bool) error { + var ai aggInfo + ai.constructor = reflect.ValueOf(impl) + t := ai.constructor.Type() + if t.Kind() != reflect.Func { + return errors.New("non-function passed to RegisterAggregator") + } + if t.NumOut() != 1 && t.NumOut() != 2 { + return errors.New("SQLite aggregator constructors must return 1 or 2 values") + } + if t.NumOut() == 2 && !t.Out(1).Implements(reflect.TypeOf((*error)(nil)).Elem()) { + return errors.New("Second return value of SQLite function must be error") + } + if t.NumIn() != 0 { + return errors.New("SQLite aggregator constructors must not have arguments") + } + + agg := t.Out(0) + switch agg.Kind() { + case reflect.Ptr, reflect.Interface: + default: + return errors.New("SQlite aggregator constructor must return a pointer object") + } + stepFn, found := agg.MethodByName("Step") + if !found { + return errors.New("SQlite aggregator doesn't have a Step() function") + } + step := stepFn.Type + if step.NumOut() != 0 && step.NumOut() != 1 { + return errors.New("SQlite aggregator Step() function must return 0 or 1 values") + } + if step.NumOut() == 1 && !step.Out(0).Implements(reflect.TypeOf((*error)(nil)).Elem()) { + return errors.New("type of SQlite aggregator Step() return value must be error") + } + + stepNArgs := step.NumIn() + start := 0 + if agg.Kind() == reflect.Ptr { + // Skip over the method receiver + stepNArgs-- + start++ + } + if step.IsVariadic() { + stepNArgs-- + } + for i := start; i < start+stepNArgs; i++ { + conv, err := callbackArg(step.In(i)) + if err != nil { + return err + } + ai.stepArgConverters = append(ai.stepArgConverters, conv) + } + if step.IsVariadic() { + conv, err := callbackArg(step.In(start + stepNArgs).Elem()) + if err != nil { + return err + } + ai.stepVariadicConverter = conv + // Pass -1 to sqlite so that it allows any number of + // arguments. The call helper verifies that the minimum number + // of arguments is present for variadic functions. + stepNArgs = -1 + } + + doneFn, found := agg.MethodByName("Done") + if !found { + return errors.New("SQlite aggregator doesn't have a Done() function") + } + done := doneFn.Type + doneNArgs := done.NumIn() + if agg.Kind() == reflect.Ptr { + // Skip over the method receiver + doneNArgs-- + } + if doneNArgs != 0 { + return errors.New("SQlite aggregator Done() function must have no arguments") + } + if done.NumOut() != 1 && done.NumOut() != 2 { + return errors.New("SQLite aggregator Done() function must return 1 or 2 values") + } + if done.NumOut() == 2 && !done.Out(1).Implements(reflect.TypeOf((*error)(nil)).Elem()) { + return errors.New("second return value of SQLite aggregator Done() function must be error") + } + + conv, err := callbackRet(done.Out(0)) + if err != nil { + return err + } + ai.doneRetConverter = conv + ai.active = make(map[int64]reflect.Value) + ai.next = 1 + + // ai must outlast the database connection, or we'll have dangling pointers. + c.aggregators = append(c.aggregators, &ai) + + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + opts := C.SQLITE_UTF8 + 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) + if rv != C.SQLITE_OK { + return c.lastError() + } + return nil +} + +// AutoCommit return which currently auto commit or not. +func (c *SQLiteConn) AutoCommit() bool { + c.mu.Lock() + defer c.mu.Unlock() + return int(C.sqlite3_get_autocommit(c.db)) != 0 +} + +func (c *SQLiteConn) lastError() error { + return lastError(c.db) +} + +// Note: may be called with db == nil +func lastError(db *C.sqlite3) error { + rv := C.sqlite3_errcode(db) // returns SQLITE_NOMEM if db == nil + if rv == C.SQLITE_OK { + return nil + } + extrv := C.sqlite3_extended_errcode(db) // returns SQLITE_NOMEM if db == nil + errStr := C.GoString(C.sqlite3_errmsg(db)) // returns "out of memory" if db == nil + + // https://www.sqlite.org/c3ref/system_errno.html + // sqlite3_system_errno is only meaningful if the error code was SQLITE_CANTOPEN, + // or it was SQLITE_IOERR and the extended code was not SQLITE_IOERR_NOMEM + var systemErrno syscall.Errno + if rv == C.SQLITE_CANTOPEN || (rv == C.SQLITE_IOERR && extrv != C.SQLITE_IOERR_NOMEM) { + systemErrno = syscall.Errno(C.sqlite3_system_errno(db)) + } + + return Error{ + Code: ErrNo(rv), + ExtendedCode: ErrNoExtended(extrv), + SystemErrno: systemErrno, + err: errStr, + } +} + +// Exec implements Execer. +func (c *SQLiteConn) Exec(query string, args []driver.Value) (driver.Result, error) { + list := make([]driver.NamedValue, len(args)) + for i, v := range args { + list[i] = driver.NamedValue{ + Ordinal: i + 1, + Value: v, + } + } + return c.exec(context.Background(), query, list) +} + +func (c *SQLiteConn) exec(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) { + start := 0 + for { + s, err := c.prepare(ctx, query) + if err != nil { + return nil, err + } + var res driver.Result + if s.(*SQLiteStmt).s != nil { + stmtArgs := make([]driver.NamedValue, 0, len(args)) + na := s.NumInput() + if len(args)-start < na { + s.Close() + return nil, fmt.Errorf("not enough args to execute query: want %d got %d", na, len(args)) + } + // consume the number of arguments used in the current + // statement and append all named arguments not + // contained therein + if len(args[start:start+na]) > 0 { + stmtArgs = append(stmtArgs, args[start:start+na]...) + for i := range args { + if (i < start || i >= na) && args[i].Name != "" { + stmtArgs = append(stmtArgs, args[i]) + } + } + for i := range stmtArgs { + stmtArgs[i].Ordinal = i + 1 + } + } + res, err = s.(*SQLiteStmt).exec(ctx, stmtArgs) + if err != nil && err != driver.ErrSkip { + s.Close() + return nil, err + } + start += na + } + tail := s.(*SQLiteStmt).t + s.Close() + if tail == "" { + if res == nil { + // https://github.com/mattn/go-sqlite3/issues/963 + res = &SQLiteResult{0, 0} + } + return res, nil + } + query = tail + } +} + +// Query implements Queryer. +func (c *SQLiteConn) Query(query string, args []driver.Value) (driver.Rows, error) { + list := make([]driver.NamedValue, len(args)) + for i, v := range args { + list[i] = driver.NamedValue{ + Ordinal: i + 1, + Value: v, + } + } + return c.query(context.Background(), query, list) +} + +func (c *SQLiteConn) query(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) { + start := 0 + for { + stmtArgs := make([]driver.NamedValue, 0, len(args)) + s, err := c.prepare(ctx, query) + if err != nil { + return nil, err + } + s.(*SQLiteStmt).cls = true + na := s.NumInput() + if len(args)-start < na { + return nil, fmt.Errorf("not enough args to execute query: want %d got %d", na, len(args)-start) + } + // consume the number of arguments used in the current + // statement and append all named arguments not contained + // therein + stmtArgs = append(stmtArgs, args[start:start+na]...) + for i := range args { + if (i < start || i >= na) && args[i].Name != "" { + stmtArgs = append(stmtArgs, args[i]) + } + } + for i := range stmtArgs { + stmtArgs[i].Ordinal = i + 1 + } + rows, err := s.(*SQLiteStmt).query(ctx, stmtArgs) + if err != nil && err != driver.ErrSkip { + s.Close() + return rows, err + } + start += na + tail := s.(*SQLiteStmt).t + if tail == "" { + return rows, nil + } + rows.Close() + s.Close() + query = tail + } +} + +// Begin transaction. +func (c *SQLiteConn) Begin() (driver.Tx, error) { + return c.begin(context.Background()) +} + +func (c *SQLiteConn) begin(ctx context.Context) (driver.Tx, error) { + if _, err := c.exec(ctx, c.txlock, nil); err != nil { + return nil, err + } + return &SQLiteTx{c}, nil +} + +// Open database and return a new connection. +// +// A pragma can take either zero or one argument. +// The argument is may be either in parentheses or it may be separated from +// the pragma name by an equal sign. The two syntaxes yield identical results. +// In many pragmas, the argument is a boolean. The boolean can be one of: +// +// 1 yes true on +// 0 no false off +// +// You can specify a DSN string using a URI as the filename. +// +// test.db +// file:test.db?cache=shared&mode=memory +// :memory: +// file::memory: +// +// mode +// Access mode of the database. +// https://www.sqlite.org/c3ref/open.html +// Values: +// - ro +// - rw +// - rwc +// - memory +// +// cache +// SQLite Shared-Cache Mode +// https://www.sqlite.org/sharedcache.html +// Values: +// - shared +// - private +// +// immutable=Boolean +// The immutable parameter is a boolean query parameter that indicates +// that the database file is stored on read-only media. When immutable is set, +// SQLite assumes that the database file cannot be changed, +// even by a process with higher privilege, +// and so the database is opened read-only and all locking and change detection is disabled. +// Caution: Setting the immutable property on a database file that +// does in fact change can result in incorrect query results and/or SQLITE_CORRUPT errors. +// +// go-sqlite3 adds the following query parameters to those used by SQLite: +// +// _loc=XXX +// Specify location of time format. It's possible to specify "auto". +// +// _mutex=XXX +// Specify mutex mode. XXX can be "no", "full". +// +// _txlock=XXX +// Specify locking behavior for transactions. XXX can be "immediate", +// "deferred", "exclusive". +// +// _auto_vacuum=X | _vacuum=X +// 0 | none - Auto Vacuum disabled +// 1 | full - Auto Vacuum FULL +// 2 | incremental - Auto Vacuum Incremental +// +// _busy_timeout=XXX"| _timeout=XXX +// Specify value for sqlite3_busy_timeout. +// +// _case_sensitive_like=Boolean | _cslike=Boolean +// https://www.sqlite.org/pragma.html#pragma_case_sensitive_like +// Default or disabled the LIKE operation is case-insensitive. +// When enabling this options behaviour of LIKE will become case-sensitive. +// +// _defer_foreign_keys=Boolean | _defer_fk=Boolean +// Defer Foreign Keys until outermost transaction is committed. +// +// _foreign_keys=Boolean | _fk=Boolean +// Enable or disable enforcement of foreign keys. +// +// _ignore_check_constraints=Boolean +// This pragma enables or disables the enforcement of CHECK constraints. +// The default setting is off, meaning that CHECK constraints are enforced by default. +// +// _journal_mode=MODE | _journal=MODE +// Set journal mode for the databases associated with the current connection. +// https://www.sqlite.org/pragma.html#pragma_journal_mode +// +// _locking_mode=X | _locking=X +// Sets the database connection locking-mode. +// The locking-mode is either NORMAL or EXCLUSIVE. +// https://www.sqlite.org/pragma.html#pragma_locking_mode +// +// _query_only=Boolean +// The query_only pragma prevents all changes to database files when enabled. +// +// _recursive_triggers=Boolean | _rt=Boolean +// Enable or disable recursive triggers. +// +// _secure_delete=Boolean|FAST +// When secure_delete is on, SQLite overwrites deleted content with zeros. +// https://www.sqlite.org/pragma.html#pragma_secure_delete +// +// _synchronous=X | _sync=X +// Change the setting of the "synchronous" flag. +// https://www.sqlite.org/pragma.html#pragma_synchronous +// +// _writable_schema=Boolean +// When this pragma is on, the SQLITE_MASTER tables in which database +// can be changed using ordinary UPDATE, INSERT, and DELETE statements. +// Warning: misuse of this pragma can easily result in a corrupt database file. +func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { + if C.sqlite3_threadsafe() == 0 { + return nil, errors.New("sqlite library was not compiled for thread-safe operation") + } + + var pkey string + + // Options + var loc *time.Location + // authCreate := false + // authUser := "" + // authPass := "" + // authCrypt := "" + // authSalt := "" + mutex := C.int(C.SQLITE_OPEN_FULLMUTEX) + txlock := "BEGIN" + + // PRAGMA's + autoVacuum := -1 + busyTimeout := 5000 + caseSensitiveLike := -1 + deferForeignKeys := -1 + foreignKeys := -1 + ignoreCheckConstraints := -1 + var journalMode string + lockingMode := "NORMAL" + queryOnly := -1 + recursiveTriggers := -1 + secureDelete := "DEFAULT" + synchronousMode := "NORMAL" + writableSchema := -1 + vfsName := "" + var cacheSize *int64 + + pos := strings.IndexRune(dsn, '?') + if pos >= 1 { + params, err := url.ParseQuery(dsn[pos+1:]) + if err != nil { + return nil, err + } + + /* + // Authentication + if _, ok := params["_auth"]; ok { + authCreate = true + } + if val := params.Get("_auth_user"); val != "" { + authUser = val + } + if val := params.Get("_auth_pass"); val != "" { + authPass = val + } + if val := params.Get("_auth_crypt"); val != "" { + authCrypt = val + } + if val := params.Get("_auth_salt"); val != "" { + authSalt = val + } + */ + + // _loc + if val := params.Get("_loc"); val != "" { + switch strings.ToLower(val) { + case "auto": + loc = time.Local + default: + loc, err = time.LoadLocation(val) + if err != nil { + return nil, fmt.Errorf("Invalid _loc: %v: %v", val, err) + } + } + } + + // _mutex + if val := params.Get("_mutex"); val != "" { + switch strings.ToLower(val) { + case "no": + mutex = C.SQLITE_OPEN_NOMUTEX + case "full": + mutex = C.SQLITE_OPEN_FULLMUTEX + default: + return nil, fmt.Errorf("Invalid _mutex: %v", val) + } + } + + // _txlock + if val := params.Get("_txlock"); val != "" { + switch strings.ToLower(val) { + case "immediate": + txlock = "BEGIN IMMEDIATE" + case "exclusive": + txlock = "BEGIN EXCLUSIVE" + case "deferred": + txlock = "BEGIN" + default: + return nil, fmt.Errorf("Invalid _txlock: %v", val) + } + } + + // Auto Vacuum (_vacuum) + // + // https://www.sqlite.org/pragma.html#pragma_auto_vacuum + // + pkey = "" // Reset pkey + if _, ok := params["_auto_vacuum"]; ok { + pkey = "_auto_vacuum" + } + if _, ok := params["_vacuum"]; ok { + pkey = "_vacuum" + } + if val := params.Get(pkey); val != "" { + switch strings.ToLower(val) { + case "0", "none": + autoVacuum = 0 + case "1", "full": + autoVacuum = 1 + case "2", "incremental": + autoVacuum = 2 + default: + return nil, fmt.Errorf("Invalid _auto_vacuum: %v, expecting value of '0 NONE 1 FULL 2 INCREMENTAL'", val) + } + } + + // Busy Timeout (_busy_timeout) + // + // https://www.sqlite.org/pragma.html#pragma_busy_timeout + // + pkey = "" // Reset pkey + if _, ok := params["_busy_timeout"]; ok { + pkey = "_busy_timeout" + } + if _, ok := params["_timeout"]; ok { + pkey = "_timeout" + } + if val := params.Get(pkey); val != "" { + iv, err := strconv.ParseInt(val, 10, 64) + if err != nil { + return nil, fmt.Errorf("Invalid _busy_timeout: %v: %v", val, err) + } + busyTimeout = int(iv) + } + + // Case Sensitive Like (_cslike) + // + // https://www.sqlite.org/pragma.html#pragma_case_sensitive_like + // + pkey = "" // Reset pkey + if _, ok := params["_case_sensitive_like"]; ok { + pkey = "_case_sensitive_like" + } + if _, ok := params["_cslike"]; ok { + pkey = "_cslike" + } + if val := params.Get(pkey); val != "" { + switch strings.ToLower(val) { + case "0", "no", "false", "off": + caseSensitiveLike = 0 + case "1", "yes", "true", "on": + caseSensitiveLike = 1 + default: + return nil, fmt.Errorf("Invalid _case_sensitive_like: %v, expecting boolean value of '0 1 false true no yes off on'", val) + } + } + + // Defer Foreign Keys (_defer_foreign_keys | _defer_fk) + // + // https://www.sqlite.org/pragma.html#pragma_defer_foreign_keys + // + pkey = "" // Reset pkey + if _, ok := params["_defer_foreign_keys"]; ok { + pkey = "_defer_foreign_keys" + } + if _, ok := params["_defer_fk"]; ok { + pkey = "_defer_fk" + } + if val := params.Get(pkey); val != "" { + switch strings.ToLower(val) { + case "0", "no", "false", "off": + deferForeignKeys = 0 + case "1", "yes", "true", "on": + deferForeignKeys = 1 + default: + return nil, fmt.Errorf("Invalid _defer_foreign_keys: %v, expecting boolean value of '0 1 false true no yes off on'", val) + } + } + + // Foreign Keys (_foreign_keys | _fk) + // + // https://www.sqlite.org/pragma.html#pragma_foreign_keys + // + pkey = "" // Reset pkey + if _, ok := params["_foreign_keys"]; ok { + pkey = "_foreign_keys" + } + if _, ok := params["_fk"]; ok { + pkey = "_fk" + } + if val := params.Get(pkey); val != "" { + switch strings.ToLower(val) { + case "0", "no", "false", "off": + foreignKeys = 0 + case "1", "yes", "true", "on": + foreignKeys = 1 + default: + return nil, fmt.Errorf("Invalid _foreign_keys: %v, expecting boolean value of '0 1 false true no yes off on'", val) + } + } + + // Ignore CHECK Constrains (_ignore_check_constraints) + // + // https://www.sqlite.org/pragma.html#pragma_ignore_check_constraints + // + if val := params.Get("_ignore_check_constraints"); val != "" { + switch strings.ToLower(val) { + case "0", "no", "false", "off": + ignoreCheckConstraints = 0 + case "1", "yes", "true", "on": + ignoreCheckConstraints = 1 + default: + return nil, fmt.Errorf("Invalid _ignore_check_constraints: %v, expecting boolean value of '0 1 false true no yes off on'", val) + } + } + + // Journal Mode (_journal_mode | _journal) + // + // https://www.sqlite.org/pragma.html#pragma_journal_mode + // + pkey = "" // Reset pkey + if _, ok := params["_journal_mode"]; ok { + pkey = "_journal_mode" + } + if _, ok := params["_journal"]; ok { + pkey = "_journal" + } + if val := params.Get(pkey); val != "" { + switch strings.ToUpper(val) { + case "DELETE", "TRUNCATE", "PERSIST", "MEMORY", "OFF": + journalMode = strings.ToUpper(val) + case "WAL": + journalMode = strings.ToUpper(val) + + // For WAL Mode set Synchronous Mode to 'NORMAL' + // See https://www.sqlite.org/pragma.html#pragma_synchronous + synchronousMode = "NORMAL" + default: + return nil, fmt.Errorf("Invalid _journal: %v, expecting value of 'DELETE TRUNCATE PERSIST MEMORY WAL OFF'", val) + } + } + + // Locking Mode (_locking) + // + // https://www.sqlite.org/pragma.html#pragma_locking_mode + // + pkey = "" // Reset pkey + if _, ok := params["_locking_mode"]; ok { + pkey = "_locking_mode" + } + if _, ok := params["_locking"]; ok { + pkey = "_locking" + } + if val := params.Get(pkey); val != "" { + switch strings.ToUpper(val) { + case "NORMAL", "EXCLUSIVE": + lockingMode = strings.ToUpper(val) + default: + return nil, fmt.Errorf("Invalid _locking_mode: %v, expecting value of 'NORMAL EXCLUSIVE", val) + } + } + + // Query Only (_query_only) + // + // https://www.sqlite.org/pragma.html#pragma_query_only + // + if val := params.Get("_query_only"); val != "" { + switch strings.ToLower(val) { + case "0", "no", "false", "off": + queryOnly = 0 + case "1", "yes", "true", "on": + queryOnly = 1 + default: + return nil, fmt.Errorf("Invalid _query_only: %v, expecting boolean value of '0 1 false true no yes off on'", val) + } + } + + // Recursive Triggers (_recursive_triggers) + // + // https://www.sqlite.org/pragma.html#pragma_recursive_triggers + // + pkey = "" // Reset pkey + if _, ok := params["_recursive_triggers"]; ok { + pkey = "_recursive_triggers" + } + if _, ok := params["_rt"]; ok { + pkey = "_rt" + } + if val := params.Get(pkey); val != "" { + switch strings.ToLower(val) { + case "0", "no", "false", "off": + recursiveTriggers = 0 + case "1", "yes", "true", "on": + recursiveTriggers = 1 + default: + return nil, fmt.Errorf("Invalid _recursive_triggers: %v, expecting boolean value of '0 1 false true no yes off on'", val) + } + } + + // Secure Delete (_secure_delete) + // + // https://www.sqlite.org/pragma.html#pragma_secure_delete + // + if val := params.Get("_secure_delete"); val != "" { + switch strings.ToLower(val) { + case "0", "no", "false", "off": + secureDelete = "OFF" + case "1", "yes", "true", "on": + secureDelete = "ON" + case "fast": + secureDelete = "FAST" + default: + return nil, fmt.Errorf("Invalid _secure_delete: %v, expecting boolean value of '0 1 false true no yes off on fast'", val) + } + } + + // Synchronous Mode (_synchronous | _sync) + // + // https://www.sqlite.org/pragma.html#pragma_synchronous + // + pkey = "" // Reset pkey + if _, ok := params["_synchronous"]; ok { + pkey = "_synchronous" + } + if _, ok := params["_sync"]; ok { + pkey = "_sync" + } + if val := params.Get(pkey); val != "" { + switch strings.ToUpper(val) { + case "0", "OFF", "1", "NORMAL", "2", "FULL", "3", "EXTRA": + synchronousMode = strings.ToUpper(val) + default: + return nil, fmt.Errorf("Invalid _synchronous: %v, expecting value of '0 OFF 1 NORMAL 2 FULL 3 EXTRA'", val) + } + } + + // Writable Schema (_writeable_schema) + // + // https://www.sqlite.org/pragma.html#pragma_writeable_schema + // + if val := params.Get("_writable_schema"); val != "" { + switch strings.ToLower(val) { + case "0", "no", "false", "off": + writableSchema = 0 + case "1", "yes", "true", "on": + writableSchema = 1 + default: + return nil, fmt.Errorf("Invalid _writable_schema: %v, expecting boolean value of '0 1 false true no yes off on'", val) + } + } + + // Cache size (_cache_size) + // + // https://sqlite.org/pragma.html#pragma_cache_size + // + if val := params.Get("_cache_size"); val != "" { + iv, err := strconv.ParseInt(val, 10, 64) + if err != nil { + return nil, fmt.Errorf("Invalid _cache_size: %v: %v", val, err) + } + cacheSize = &iv + } + + if val := params.Get("vfs"); val != "" { + vfsName = val + } + + if !strings.HasPrefix(dsn, "file:") { + dsn = dsn[:pos] + } + } + + var db *C.sqlite3 + name := C.CString(dsn) + defer C.free(unsafe.Pointer(name)) + var vfs *C.char + if vfsName != "" { + 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) + if rv != 0 { + // Save off the error _before_ closing the database. + // This is safe even if db is nil. + err := lastError(db) + if db != nil { + C.sqlite3_close_v2(db) + } + return nil, err + } + if db == nil { + return nil, errors.New("sqlite succeeded without returning a database") + } + + exec := func(s string) error { + cs := C.CString(s) + rv := C.sqlite3_exec(db, cs, nil, nil, nil) + C.free(unsafe.Pointer(cs)) + if rv != C.SQLITE_OK { + return lastError(db) + } + return nil + } + + // Busy timeout + if err := exec(fmt.Sprintf("PRAGMA busy_timeout = %d;", busyTimeout)); err != nil { + C.sqlite3_close_v2(db) + return nil, err + } + + // USER AUTHENTICATION + // + // User Authentication is always performed even when + // sqlite_userauth is not compiled in, because without user authentication + // the authentication is a no-op. + // + // Workflow + // - Authenticate + // ON::SUCCESS => Continue + // ON::SQLITE_AUTH => Return error and exit Open(...) + // + // - Activate User Authentication + // Check if the user wants to activate User Authentication. + // If so then first create a temporary AuthConn to the database + // This is possible because we are already successfully authenticated. + // + // - Check if `sqlite_user`` table exists + // YES => Add the provided user from DSN as Admin User and + // activate user authentication. + // NO => Continue + // + + // Create connection to SQLite + conn := &SQLiteConn{db: db, loc: loc, txlock: txlock} + + /* + // Password Cipher has to be registered before authentication + if len(authCrypt) > 0 { + switch strings.ToUpper(authCrypt) { + case "SHA1": + if err := conn.RegisterFunc("sqlite_crypt", CryptEncoderSHA1, true); err != nil { + return nil, fmt.Errorf("CryptEncoderSHA1: %s", err) + } + case "SSHA1": + if len(authSalt) == 0 { + return nil, fmt.Errorf("_auth_crypt=ssha1, requires _auth_salt") + } + if err := conn.RegisterFunc("sqlite_crypt", CryptEncoderSSHA1(authSalt), true); err != nil { + return nil, fmt.Errorf("CryptEncoderSSHA1: %s", err) + } + case "SHA256": + if err := conn.RegisterFunc("sqlite_crypt", CryptEncoderSHA256, true); err != nil { + return nil, fmt.Errorf("CryptEncoderSHA256: %s", err) + } + case "SSHA256": + if len(authSalt) == 0 { + return nil, fmt.Errorf("_auth_crypt=ssha256, requires _auth_salt") + } + if err := conn.RegisterFunc("sqlite_crypt", CryptEncoderSSHA256(authSalt), true); err != nil { + return nil, fmt.Errorf("CryptEncoderSSHA256: %s", err) + } + case "SHA384": + if err := conn.RegisterFunc("sqlite_crypt", CryptEncoderSHA384, true); err != nil { + return nil, fmt.Errorf("CryptEncoderSHA384: %s", err) + } + case "SSHA384": + if len(authSalt) == 0 { + return nil, fmt.Errorf("_auth_crypt=ssha384, requires _auth_salt") + } + if err := conn.RegisterFunc("sqlite_crypt", CryptEncoderSSHA384(authSalt), true); err != nil { + return nil, fmt.Errorf("CryptEncoderSSHA384: %s", err) + } + case "SHA512": + if err := conn.RegisterFunc("sqlite_crypt", CryptEncoderSHA512, true); err != nil { + return nil, fmt.Errorf("CryptEncoderSHA512: %s", err) + } + case "SSHA512": + if len(authSalt) == 0 { + return nil, fmt.Errorf("_auth_crypt=ssha512, requires _auth_salt") + } + if err := conn.RegisterFunc("sqlite_crypt", CryptEncoderSSHA512(authSalt), true); err != nil { + return nil, fmt.Errorf("CryptEncoderSSHA512: %s", err) + } + } + } + + // Preform Authentication + if err := conn.Authenticate(authUser, authPass); err != nil { + return nil, err + } + + // Register: authenticate + // Authenticate will perform an authentication of the provided username + // and password against the database. + // + // If a database contains the SQLITE_USER table, then the + // call to Authenticate must be invoked with an + // appropriate username and password prior to enable read and write + //access to the database. + // + // Return SQLITE_OK on success or SQLITE_ERROR if the username/password + // combination is incorrect or unknown. + // + // If the SQLITE_USER table is not present in the database file, then + // this interface is a harmless no-op returnning SQLITE_OK. + if err := conn.RegisterFunc("authenticate", conn.authenticate, true); err != nil { + return nil, err + } + // + // Register: auth_user_add + // auth_user_add can be used (by an admin user only) + // to create a new user. When called on a no-authentication-required + // database, this routine converts the database into an authentication- + // required database, automatically makes the added user an + // administrator, and logs in the current connection as that user. + // The AuthUserAdd only works for the "main" database, not + // for any ATTACH-ed databases. Any call to AuthUserAdd by a + // non-admin user results in an error. + if err := conn.RegisterFunc("auth_user_add", conn.authUserAdd, true); err != nil { + return nil, err + } + // + // Register: auth_user_change + // auth_user_change can be used to change a users + // login credentials or admin privilege. Any user can change their own + // login credentials. Only an admin user can change another users login + // credentials or admin privilege setting. No user may change their own + // admin privilege setting. + if err := conn.RegisterFunc("auth_user_change", conn.authUserChange, true); err != nil { + return nil, err + } + // + // Register: auth_user_delete + // auth_user_delete can be used (by an admin user only) + // to delete a user. The currently logged-in user cannot be deleted, + // which guarantees that there is always an admin user and hence that + // the database cannot be converted into a no-authentication-required + // database. + if err := conn.RegisterFunc("auth_user_delete", conn.authUserDelete, true); err != nil { + return nil, err + } + + // Register: auth_enabled + // auth_enabled can be used to check if user authentication is enabled + if err := conn.RegisterFunc("auth_enabled", conn.authEnabled, true); err != nil { + return nil, err + } + */ + + // Auto Vacuum + // Moved auto_vacuum command, the user preference for auto_vacuum needs to be implemented directly after + // the authentication and before the sqlite_user table gets created if the user + // decides to activate User Authentication because + // auto_vacuum needs to be set before any tables are created + // and activating user authentication creates the internal table `sqlite_user`. + if autoVacuum > -1 { + if err := exec(fmt.Sprintf("PRAGMA auto_vacuum = %d;", autoVacuum)); err != nil { + C.sqlite3_close_v2(db) + return nil, err + } + } + + /* + // Check if user wants to activate User Authentication + if authCreate { + // Before going any further, we need to check that the user + // has provided an username and password within the DSN. + // We are not allowed to continue. + if len(authUser) == 0 { + return nil, fmt.Errorf("Missing '_auth_user' while user authentication was requested with '_auth'") + } + if len(authPass) == 0 { + return nil, fmt.Errorf("Missing '_auth_pass' while user authentication was requested with '_auth'") + } + + // Check if User Authentication is Enabled + authExists := conn.AuthEnabled() + if !authExists { + if err := conn.AuthUserAdd(authUser, authPass, true); err != nil { + return nil, err + } + } + } + */ + + // Case Sensitive LIKE + if caseSensitiveLike > -1 { + if err := exec(fmt.Sprintf("PRAGMA case_sensitive_like = %d;", caseSensitiveLike)); err != nil { + C.sqlite3_close_v2(db) + return nil, err + } + } + + // Defer Foreign Keys + if deferForeignKeys > -1 { + if err := exec(fmt.Sprintf("PRAGMA defer_foreign_keys = %d;", deferForeignKeys)); err != nil { + C.sqlite3_close_v2(db) + return nil, err + } + } + + // Foreign Keys + if foreignKeys > -1 { + if err := exec(fmt.Sprintf("PRAGMA foreign_keys = %d;", foreignKeys)); err != nil { + C.sqlite3_close_v2(db) + return nil, err + } + } + + // Ignore CHECK Constraints + if ignoreCheckConstraints > -1 { + if err := exec(fmt.Sprintf("PRAGMA ignore_check_constraints = %d;", ignoreCheckConstraints)); err != nil { + C.sqlite3_close_v2(db) + return nil, err + } + } + + // Journal Mode + if journalMode != "" { + if err := exec(fmt.Sprintf("PRAGMA journal_mode = %s;", journalMode)); err != nil { + C.sqlite3_close_v2(db) + return nil, err + } + } + + // Locking Mode + // Because the default is NORMAL and this is not changed in this package + // by using the compile time SQLITE_DEFAULT_LOCKING_MODE this PRAGMA can always be executed + if err := exec(fmt.Sprintf("PRAGMA locking_mode = %s;", lockingMode)); err != nil { + C.sqlite3_close_v2(db) + return nil, err + } + + // Query Only + if queryOnly > -1 { + if err := exec(fmt.Sprintf("PRAGMA query_only = %d;", queryOnly)); err != nil { + C.sqlite3_close_v2(db) + return nil, err + } + } + + // Recursive Triggers + if recursiveTriggers > -1 { + if err := exec(fmt.Sprintf("PRAGMA recursive_triggers = %d;", recursiveTriggers)); err != nil { + C.sqlite3_close_v2(db) + return nil, err + } + } + + // Secure Delete + // + // Because this package can set the compile time flag SQLITE_SECURE_DELETE with a build tag + // the default value for secureDelete var is 'DEFAULT' this way + // you can compile with secure_delete 'ON' and disable it for a specific database connection. + if secureDelete != "DEFAULT" { + if err := exec(fmt.Sprintf("PRAGMA secure_delete = %s;", secureDelete)); err != nil { + C.sqlite3_close_v2(db) + return nil, err + } + } + + // Synchronous Mode + // + // Because default is NORMAL this statement is always executed + if err := exec(fmt.Sprintf("PRAGMA synchronous = %s;", synchronousMode)); err != nil { + conn.Close() + return nil, err + } + + // Writable Schema + if writableSchema > -1 { + if err := exec(fmt.Sprintf("PRAGMA writable_schema = %d;", writableSchema)); err != nil { + C.sqlite3_close_v2(db) + return nil, err + } + } + + // Cache Size + if cacheSize != nil { + if err := exec(fmt.Sprintf("PRAGMA cache_size = %d;", *cacheSize)); err != nil { + C.sqlite3_close_v2(db) + return nil, err + } + } + + if len(d.Extensions) > 0 { + if err := conn.loadExtensions(d.Extensions); err != nil { + conn.Close() + return nil, err + } + } + + if d.ConnectHook != nil { + if err := d.ConnectHook(conn); err != nil { + conn.Close() + return nil, err + } + } + runtime.SetFinalizer(conn, (*SQLiteConn).Close) + return conn, nil +} + +// Close the connection. +func (c *SQLiteConn) Close() error { + rv := C.sqlite3_close_v2(c.db) + if rv != C.SQLITE_OK { + return c.lastError() + } + deleteHandles(c) + c.mu.Lock() + c.db = nil + c.mu.Unlock() + runtime.SetFinalizer(c, nil) + return nil +} + +func (c *SQLiteConn) dbConnOpen() bool { + if c == nil { + return false + } + c.mu.Lock() + defer c.mu.Unlock() + return c.db != nil +} + +// Prepare the query string. Return a new statement. +func (c *SQLiteConn) Prepare(query string) (driver.Stmt, error) { + return c.prepare(context.Background(), query) +} + +func (c *SQLiteConn) prepare(ctx context.Context, query string) (driver.Stmt, error) { + pquery := C.CString(query) + 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) + if rv != C.SQLITE_OK { + return nil, c.lastError() + } + var t string + if tail != nil && *tail != '\000' { + t = strings.TrimSpace(C.GoString(tail)) + } + ss := &SQLiteStmt{c: c, s: s, t: t} + runtime.SetFinalizer(ss, (*SQLiteStmt).Close) + return ss, nil +} + +// Run-Time Limit Categories. +// See: http://www.sqlite.org/c3ref/c_limit_attached.html +const ( + SQLITE_LIMIT_LENGTH = C.SQLITE_LIMIT_LENGTH + SQLITE_LIMIT_SQL_LENGTH = C.SQLITE_LIMIT_SQL_LENGTH + SQLITE_LIMIT_COLUMN = C.SQLITE_LIMIT_COLUMN + SQLITE_LIMIT_EXPR_DEPTH = C.SQLITE_LIMIT_EXPR_DEPTH + SQLITE_LIMIT_COMPOUND_SELECT = C.SQLITE_LIMIT_COMPOUND_SELECT + SQLITE_LIMIT_VDBE_OP = C.SQLITE_LIMIT_VDBE_OP + SQLITE_LIMIT_FUNCTION_ARG = C.SQLITE_LIMIT_FUNCTION_ARG + SQLITE_LIMIT_ATTACHED = C.SQLITE_LIMIT_ATTACHED + SQLITE_LIMIT_LIKE_PATTERN_LENGTH = C.SQLITE_LIMIT_LIKE_PATTERN_LENGTH + SQLITE_LIMIT_VARIABLE_NUMBER = C.SQLITE_LIMIT_VARIABLE_NUMBER + SQLITE_LIMIT_TRIGGER_DEPTH = C.SQLITE_LIMIT_TRIGGER_DEPTH + SQLITE_LIMIT_WORKER_THREADS = C.SQLITE_LIMIT_WORKER_THREADS +) + +// GetFilename returns the absolute path to the file containing +// the requested schema. When passed an empty string, it will +// instead use the database's default schema: "main". +// See: sqlite3_db_filename, https://www.sqlite.org/c3ref/db_filename.html +func (c *SQLiteConn) GetFilename(schemaName string) string { + if schemaName == "" { + schemaName = "main" + } + 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))) +} + +// 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))) +} + +// SetFileControlInt invokes the xFileControl method on a given database. The +// dbName is the name of the database. It will default to "main" if left blank. +// The op is one of the opcodes prefixed by "SQLITE_FCNTL_". The arg argument +// and return code are both opcode-specific. Please see the SQLite documentation. +// +// This method is not thread-safe as the returned error code can be changed by +// another call if invoked concurrently. +// +// See: sqlite3_file_control, https://www.sqlite.org/c3ref/file_control.html +func (c *SQLiteConn) SetFileControlInt(dbName string, op int, arg int) error { + if dbName == "" { + dbName = "main" + } + + cDBName := C.CString(dbName) + defer C.free(unsafe.Pointer(cDBName)) + + cArg := C.int(arg) + rv := C.sqlite3_file_control(c.db, cDBName, C.int(op), unsafe.Pointer(&cArg)) + if rv != C.SQLITE_OK { + return c.lastError() + } + return nil +} + +// Close the statement. +func (s *SQLiteStmt) Close() error { + s.mu.Lock() + defer s.mu.Unlock() + if s.closed { + return nil + } + s.closed = true + if !s.c.dbConnOpen() { + return errors.New("sqlite statement with already closed database connection") + } + rv := C.sqlite3_finalize(s.s) + s.s = nil + if rv != C.SQLITE_OK { + return s.c.lastError() + } + s.c = nil + runtime.SetFinalizer(s, nil) + return nil +} + +// NumInput return a number of parameters. +func (s *SQLiteStmt) NumInput() int { + return int(C.sqlite3_bind_parameter_count(s.s)) +} + +var placeHolder = []byte{0} + +func (s *SQLiteStmt) bind(args []driver.NamedValue) error { + rv := C.sqlite3_reset(s.s) + if rv != C.SQLITE_ROW && rv != C.SQLITE_OK && rv != C.SQLITE_DONE { + return s.c.lastError() + } + + bindIndices := make([][3]int, len(args)) + prefixes := []string{":", "@", "$"} + for i, v := range args { + bindIndices[i][0] = args[i].Ordinal + if v.Name != "" { + for j := range prefixes { + cname := C.CString(prefixes[j] + v.Name) + bindIndices[i][j] = int(C.sqlite3_bind_parameter_index(s.s, cname)) + C.free(unsafe.Pointer(cname)) + } + args[i].Ordinal = bindIndices[i][0] + } + } + + for i, arg := range args { + for j := range bindIndices[i] { + if bindIndices[i][j] == 0 { + continue + } + n := C.int(bindIndices[i][j]) + switch v := arg.Value.(type) { + case nil: + 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)) + } else { + b := []byte(v) + rv = C._sqlite3_bind_text(s.s, n, (*C.char)(unsafe.Pointer(&b[0])), C.int(len(b))) + } + case int64: + rv = C.sqlite3_bind_int64(s.s, n, C.sqlite3_int64(v)) + case bool: + if v { + rv = C.sqlite3_bind_int(s.s, n, 1) + } else { + rv = C.sqlite3_bind_int(s.s, n, 0) + } + case float64: + rv = C.sqlite3_bind_double(s.s, n, C.double(v)) + case []byte: + if v == nil { + rv = C.sqlite3_bind_null(s.s, n) + } else { + ln := len(v) + if ln == 0 { + v = placeHolder + } + rv = C._sqlite3_bind_blob(s.s, n, unsafe.Pointer(&v[0]), C.int(ln)) + } + 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))) + } + if rv != C.SQLITE_OK { + return s.c.lastError() + } + } + } + return nil +} + +// Query the statement with arguments. Return records. +func (s *SQLiteStmt) Query(args []driver.Value) (driver.Rows, error) { + list := make([]driver.NamedValue, len(args)) + for i, v := range args { + list[i] = driver.NamedValue{ + Ordinal: i + 1, + Value: v, + } + } + return s.query(context.Background(), list) +} + +func (s *SQLiteStmt) query(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) { + if err := s.bind(args); err != nil { + return nil, err + } + + rows := &SQLiteRows{ + s: s, + nc: int(C.sqlite3_column_count(s.s)), + cols: nil, + decltype: nil, + cls: s.cls, + closed: false, + ctx: ctx, + } + runtime.SetFinalizer(rows, (*SQLiteRows).Close) + + return rows, nil +} + +// LastInsertId return last inserted ID. +func (r *SQLiteResult) LastInsertId() (int64, error) { + return r.id, nil +} + +// RowsAffected return how many rows affected. +func (r *SQLiteResult) RowsAffected() (int64, error) { + return r.changes, nil +} + +// Exec execute the statement with arguments. Return result object. +func (s *SQLiteStmt) Exec(args []driver.Value) (driver.Result, error) { + list := make([]driver.NamedValue, len(args)) + for i, v := range args { + list[i] = driver.NamedValue{ + Ordinal: i + 1, + Value: v, + } + } + return s.exec(context.Background(), list) +} + +func isInterruptErr(err error) bool { + sqliteErr, ok := err.(Error) + if ok { + return sqliteErr.Code == ErrInterrupt + } + return false +} + +// exec executes a query that doesn't return rows. Attempts to honor context timeout. +func (s *SQLiteStmt) exec(ctx context.Context, args []driver.NamedValue) (driver.Result, error) { + if ctx.Done() == nil { + return s.execSync(args) + } + + type result struct { + r driver.Result + err error + } + resultCh := make(chan result) + defer close(resultCh) + go func() { + r, err := s.execSync(args) + resultCh <- result{r, err} + }() + var rv result + select { + case rv = <-resultCh: + case <-ctx.Done(): + select { + case rv = <-resultCh: // no need to interrupt, operation completed in db + default: + // this is still racy and can be no-op if executed between sqlite3_* calls in execSync. + C.sqlite3_interrupt(s.c.db) + rv = <-resultCh // wait for goroutine completed + if isInterruptErr(rv.err) { + return nil, ctx.Err() + } + } + } + return rv.r, rv.err +} + +func (s *SQLiteStmt) execSync(args []driver.NamedValue) (driver.Result, error) { + if err := s.bind(args); err != nil { + C.sqlite3_reset(s.s) + C.sqlite3_clear_bindings(s.s) + return nil, err + } + + var rowid, changes C.longlong + rv := C._sqlite3_step_row_internal(s.s, &rowid, &changes) + if rv != C.SQLITE_ROW && rv != C.SQLITE_OK && rv != C.SQLITE_DONE { + err := s.c.lastError() + C.sqlite3_reset(s.s) + C.sqlite3_clear_bindings(s.s) + return nil, err + } + + return &SQLiteResult{id: int64(rowid), changes: int64(changes)}, nil +} + +// Readonly reports if this statement is considered readonly by SQLite. +// +// See: https://sqlite.org/c3ref/stmt_readonly.html +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 { + rc.s.mu.Unlock() + return nil + } + rc.closed = true + if rc.cls { + rc.s.mu.Unlock() + return rc.s.Close() + } + rv := C.sqlite3_reset(rc.s.s) + if rv != C.SQLITE_OK { + rc.s.mu.Unlock() + return rc.s.c.lastError() + } + rc.s.mu.Unlock() + rc.s = nil + runtime.SetFinalizer(rc, nil) + return nil +} + +// Columns return column names. +func (rc *SQLiteRows) Columns() []string { + rc.s.mu.Lock() + defer rc.s.mu.Unlock() + if rc.s.s != nil && rc.nc != len(rc.cols) { + rc.cols = make([]string, rc.nc) + for i := 0; i < rc.nc; i++ { + rc.cols[i] = C.GoString(C.sqlite3_column_name(rc.s.s, C.int(i))) + } + } + return rc.cols +} + +func (rc *SQLiteRows) declTypes() []string { + if rc.s.s != nil && rc.decltype == nil { + rc.decltype = make([]string, rc.nc) + for i := 0; i < rc.nc; i++ { + rc.decltype[i] = strings.ToLower(C.GoString(C.sqlite3_column_decltype(rc.s.s, C.int(i)))) + } + } + return rc.decltype +} + +// DeclTypes return column types. +func (rc *SQLiteRows) DeclTypes() []string { + rc.s.mu.Lock() + defer rc.s.mu.Unlock() + return rc.declTypes() +} + +// Next move cursor to next. Attempts to honor context timeout from QueryContext call. +func (rc *SQLiteRows) Next(dest []driver.Value) error { + rc.s.mu.Lock() + defer rc.s.mu.Unlock() + + if rc.s.closed { + return io.EOF + } + + if rc.ctx.Done() == nil { + return rc.nextSyncLocked(dest) + } + resultCh := make(chan error) + defer close(resultCh) + go func() { + resultCh <- rc.nextSyncLocked(dest) + }() + select { + case err := <-resultCh: + return err + case <-rc.ctx.Done(): + select { + case <-resultCh: // no need to interrupt + default: + // this is still racy and can be no-op if executed between sqlite3_* calls in nextSyncLocked. + C.sqlite3_interrupt(rc.s.c.db) + <-resultCh // ensure goroutine completed + } + return rc.ctx.Err() + } +} + +// 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) + if rv == C.SQLITE_DONE { + return io.EOF + } + if rv != C.SQLITE_ROW { + rv = C.sqlite3_reset(rc.s.s) + if rv != C.SQLITE_OK { + return rc.s.c.lastError() + } + return nil + } + + rc.declTypes() + + for i := range dest { + switch C.sqlite3_column_type(rc.s.s, C.int(i)) { + case C.SQLITE_INTEGER: + val := int64(C.sqlite3_column_int64(rc.s.s, C.int(i))) + switch rc.decltype[i] { + case columnTimestamp, columnDatetime, columnDate: + var t time.Time + // Assume a millisecond unix timestamp if it's 13 digits -- too + // large to be a reasonable timestamp in seconds. + if val > 1e12 || val < -1e12 { + val *= int64(time.Millisecond) // convert ms to nsec + t = time.Unix(0, val) + } else { + t = time.Unix(val, 0) + } + t = t.UTC() + if rc.s.c.loc != nil { + t = t.In(rc.s.c.loc) + } + dest[i] = t + case "boolean": + dest[i] = val > 0 + default: + dest[i] = val + } + case C.SQLITE_FLOAT: + dest[i] = float64(C.sqlite3_column_double(rc.s.s, C.int(i))) + case C.SQLITE_BLOB: + p := C.sqlite3_column_blob(rc.s.s, C.int(i)) + if p == nil { + dest[i] = []byte{} + continue + } + n := C.sqlite3_column_bytes(rc.s.s, C.int(i)) + dest[i] = C.GoBytes(p, n) + case C.SQLITE_NULL: + dest[i] = nil + case C.SQLITE_TEXT: + var err error + var timeVal time.Time + + n := int(C.sqlite3_column_bytes(rc.s.s, C.int(i))) + s := C.GoStringN((*C.char)(unsafe.Pointer(C.sqlite3_column_text(rc.s.s, C.int(i)))), C.int(n)) + + switch rc.decltype[i] { + case columnTimestamp, columnDatetime, columnDate: + var t time.Time + s = strings.TrimSuffix(s, "Z") + for _, format := range SQLiteTimestampFormats { + if timeVal, err = time.ParseInLocation(format, s, time.UTC); err == nil { + t = timeVal + break + } + } + if err != nil { + // The column is a time value, so return the zero time on parse failure. + t = time.Time{} + } + if rc.s.c.loc != nil { + t = t.In(rc.s.c.loc) + } + dest[i] = t + default: + dest[i] = s + } + } + } + return nil +} +// Copyright (C) 2019 Yasuhiro Matsumoto <mattn.jp@gmail.com>. +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +const i64 = unsafe.Sizeof(int(0)) > 4 + +// SQLiteContext behave sqlite3_context +type SQLiteContext C.sqlite3_context + +// ResultBool sets the result of an SQL function. +func (c *SQLiteContext) ResultBool(b bool) { + if b { + c.ResultInt(1) + } else { + c.ResultInt(0) + } +} + +// ResultBlob sets the result of an SQL function. +// See: sqlite3_result_blob, http://sqlite.org/c3ref/result_blob.html +func (c *SQLiteContext) ResultBlob(b []byte) { + if i64 && len(b) > math.MaxInt32 { + C.sqlite3_result_error_toobig((*C.sqlite3_context)(c)) + return + } + var p *byte + if len(b) > 0 { + p = &b[0] + } + C.my_result_blob((*C.sqlite3_context)(c), unsafe.Pointer(p), C.int(len(b))) +} + +// ResultDouble sets the result of an SQL function. +// See: sqlite3_result_double, http://sqlite.org/c3ref/result_blob.html +func (c *SQLiteContext) ResultDouble(d float64) { + C.sqlite3_result_double((*C.sqlite3_context)(c), C.double(d)) +} + +// ResultInt sets the result of an SQL function. +// See: sqlite3_result_int, http://sqlite.org/c3ref/result_blob.html +func (c *SQLiteContext) ResultInt(i int) { + if i64 && (i > math.MaxInt32 || i < math.MinInt32) { + C.sqlite3_result_int64((*C.sqlite3_context)(c), C.sqlite3_int64(i)) + } else { + C.sqlite3_result_int((*C.sqlite3_context)(c), C.int(i)) + } +} + +// ResultInt64 sets the result of an SQL function. +// See: sqlite3_result_int64, http://sqlite.org/c3ref/result_blob.html +func (c *SQLiteContext) ResultInt64(i int64) { + C.sqlite3_result_int64((*C.sqlite3_context)(c), C.sqlite3_int64(i)) +} + +// ResultNull sets the result of an SQL function. +// See: sqlite3_result_null, http://sqlite.org/c3ref/result_blob.html +func (c *SQLiteContext) ResultNull() { + C.sqlite3_result_null((*C.sqlite3_context)(c)) +} + +// ResultText sets the result of an SQL function. +// See: sqlite3_result_text, http://sqlite.org/c3ref/result_blob.html +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) +} + +// ResultZeroblob sets the result of an SQL function. +// See: sqlite3_result_zeroblob, http://sqlite.org/c3ref/result_blob.html +func (c *SQLiteContext) ResultZeroblob(n int) { + C.sqlite3_result_zeroblob((*C.sqlite3_context)(c), C.int(n)) +} +// Copyright (C) 2018 G.J.R. Timmer <gjr.timmer@gmail.com>. +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +// This file provides several different implementations for the +// default embedded sqlite_crypt function. +// This function is uses a caesar-cypher by default +// and is used within the UserAuthentication module to encode +// the password. +// +// The provided functions can be used as an overload to the sqlite_crypt +// function through the use of the RegisterFunc on the connection. +// +// Because the functions can serv a purpose to an end-user +// without using the UserAuthentication module +// the functions are default compiled in. +// +// From SQLITE3 - user-auth.txt +// The sqlite_user.pw field is encoded by a built-in SQL function +// "sqlite_crypt(X,Y)". The two arguments are both BLOBs. The first argument +// is the plaintext password supplied to the sqlite3_user_authenticate() +// interface. The second argument is the sqlite_user.pw value and is supplied +// so that the function can extract the "salt" used by the password encoder. +// The result of sqlite_crypt(X,Y) is another blob which is the value that +// ends up being stored in sqlite_user.pw. To verify credentials X supplied +// by the sqlite3_user_authenticate() routine, SQLite runs: +// +// sqlite_user.pw == sqlite_crypt(X, sqlite_user.pw) +// +// To compute an appropriate sqlite_user.pw value from a new or modified +// password X, sqlite_crypt(X,NULL) is run. A new random salt is selected +// when the second argument is NULL. +// +// The built-in version of of sqlite_crypt() uses a simple Caesar-cypher +// which prevents passwords from being revealed by searching the raw database +// for ASCII text, but is otherwise trivally broken. For better password +// security, the database should be encrypted using the SQLite Encryption +// Extension or similar technology. Or, the application can use the +// sqlite3_create_function() interface to provide an alternative +// implementation of sqlite_crypt() that computes a stronger password hash, +// perhaps using a cryptographic hash function like SHA1. + +// CryptEncoderSHA1 encodes a password with SHA1 +func CryptEncoderSHA1(pass []byte, hash any) []byte { + h := sha1.Sum(pass) + return h[:] +} + +// CryptEncoderSSHA1 encodes a password with SHA1 with the +// configured salt. +func CryptEncoderSSHA1(salt string) func(pass []byte, hash any) []byte { + return func(pass []byte, hash any) []byte { + s := []byte(salt) + p := append(pass, s...) + h := sha1.Sum(p) + return h[:] + } +} + +// CryptEncoderSHA256 encodes a password with SHA256 +func CryptEncoderSHA256(pass []byte, hash any) []byte { + h := sha256.Sum256(pass) + return h[:] +} + +// CryptEncoderSSHA256 encodes a password with SHA256 +// with the configured salt +func CryptEncoderSSHA256(salt string) func(pass []byte, hash any) []byte { + return func(pass []byte, hash any) []byte { + s := []byte(salt) + p := append(pass, s...) + h := sha256.Sum256(p) + return h[:] + } +} + +// CryptEncoderSHA384 encodes a password with SHA384 +func CryptEncoderSHA384(pass []byte, hash any) []byte { + h := sha512.Sum384(pass) + return h[:] +} + +// CryptEncoderSSHA384 encodes a password with SHA384 +// with the configured salt +func CryptEncoderSSHA384(salt string) func(pass []byte, hash any) []byte { + return func(pass []byte, hash any) []byte { + s := []byte(salt) + p := append(pass, s...) + h := sha512.Sum384(p) + return h[:] + } +} + +// CryptEncoderSHA512 encodes a password with SHA512 +func CryptEncoderSHA512(pass []byte, hash any) []byte { + h := sha512.Sum512(pass) + return h[:] +} + +// CryptEncoderSSHA512 encodes a password with SHA512 +// with the configured salt +func CryptEncoderSSHA512(salt string) func(pass []byte, hash any) []byte { + return func(pass []byte, hash any) []byte { + s := []byte(salt) + p := append(pass, s...) + h := sha512.Sum512(p) + return h[:] + } +} + +// EOF +// Copyright (C) 2019 Yasuhiro Matsumoto <mattn.jp@gmail.com>. +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +// //go:build cgo && go1.8 // +build cgo,go1.8 + +// Ping implement Pinger. +func (c *SQLiteConn) Ping(ctx context.Context) error { + if c.db == nil { + // must be ErrBadConn for sql to close the database + return driver.ErrBadConn + } + return nil +} + +// QueryContext implement QueryerContext. +func (c *SQLiteConn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) { + return c.query(ctx, query, args) +} + +// ExecContext implement ExecerContext. +func (c *SQLiteConn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) { + return c.exec(ctx, query, args) +} + +// PrepareContext implement ConnPrepareContext. +func (c *SQLiteConn) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) { + return c.prepare(ctx, query) +} + +// BeginTx implement ConnBeginTx. +func (c *SQLiteConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) { + return c.begin(ctx) +} + +// QueryContext implement QueryerContext. +func (s *SQLiteStmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) { + return s.query(ctx, args) +} + +// ExecContext implement ExecerContext. +func (s *SQLiteStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) { + return s.exec(ctx, args) +} +// Copyright (C) 2019 Yasuhiro Matsumoto <mattn.jp@gmail.com>. +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +// //go:build libsqlite3 // +build libsqlite3 + +// Copyright (C) 2019 Yasuhiro Matsumoto <mattn.jp@gmail.com>. +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +// //go:build !sqlite_omit_load_extension // +build !sqlite_omit_load_extension + +func (c *SQLiteConn) loadExtensions(extensions []string) error { + rv := C.sqlite3_enable_load_extension(c.db, 1) + if rv != C.SQLITE_OK { + return errors.New(C.GoString(C.sqlite3_errmsg(c.db))) + } + + for _, extension := range extensions { + if err := c.loadExtension(extension, nil); err != nil { + C.sqlite3_enable_load_extension(c.db, 0) + return err + } + } + + rv = C.sqlite3_enable_load_extension(c.db, 0) + if rv != C.SQLITE_OK { + return errors.New(C.GoString(C.sqlite3_errmsg(c.db))) + } + + return nil +} + +// LoadExtension load the sqlite3 extension. +func (c *SQLiteConn) LoadExtension(lib string, entry string) error { + rv := C.sqlite3_enable_load_extension(c.db, 1) + if rv != C.SQLITE_OK { + return errors.New(C.GoString(C.sqlite3_errmsg(c.db))) + } + + if err := c.loadExtension(lib, &entry); err != nil { + C.sqlite3_enable_load_extension(c.db, 0) + return err + } + + rv = C.sqlite3_enable_load_extension(c.db, 0) + if rv != C.SQLITE_OK { + return errors.New(C.GoString(C.sqlite3_errmsg(c.db))) + } + + return nil +} + +func (c *SQLiteConn) loadExtension(lib string, entry *string) error { + clib := C.CString(lib) + defer C.free(unsafe.Pointer(clib)) + + var centry *C.char + if entry != nil { + centry = C.CString(*entry) + defer C.free(unsafe.Pointer(centry)) + } + + var errMsg *C.char + defer C.sqlite3_free(unsafe.Pointer(errMsg)) + + rv := C.sqlite3_load_extension(c.db, clib, centry, &errMsg) + if rv != C.SQLITE_OK { + return errors.New(C.GoString(errMsg)) + } + + return nil +} +// Copyright (C) 2019 Yasuhiro Matsumoto <mattn.jp@gmail.com>. +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +// //go:build sqlite_omit_load_extension // +build sqlite_omit_load_extension + +/* +func (c *SQLiteConn) loadExtensions(extensions []string) error { + return errors.New("Extensions have been disabled for static builds") +} + +func (c *SQLiteConn) LoadExtension(lib string, entry string) error { + return errors.New("Extensions have been disabled for static builds") +} +*/ +// Copyright (C) 2019 Yasuhiro Matsumoto <mattn.jp@gmail.com>. +// Copyright (C) 2018 G.J.R. Timmer <gjr.timmer@gmail.com>. +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +// //go:build sqlite_allow_uri_authority // +build sqlite_allow_uri_authority + +// Copyright (C) 2019 Yasuhiro Matsumoto <mattn.jp@gmail.com>. +// Copyright (C) 2018 G.J.R. Timmer <gjr.timmer@gmail.com>. +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +// //go:build !windows && sqlite_app_armor // +build !windows,sqlite_app_armor + +// //go:build sqlite_column_metadata // +build sqlite_column_metadata + +// ColumnTableName returns the table that is the origin of a particular result +// column in a SELECT statement. +// +// See https://www.sqlite.org/c3ref/column_database_name.html +func (s *SQLiteStmt) ColumnTableName(n int) string { + return C.GoString(C.sqlite3_column_table_name(s.s, C.int(n))) +} +// Copyright (C) 2019 Yasuhiro Matsumoto <mattn.jp@gmail.com>. +// Copyright (C) 2018 G.J.R. Timmer <gjr.timmer@gmail.com>. +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +// //go:build sqlite_foreign_keys // +build sqlite_foreign_keys + +// Copyright (C) 2019 Yasuhiro Matsumoto <mattn.jp@gmail.com>. +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +// //go:build sqlite_fts5 || fts5 // +build sqlite_fts5 fts5 + +// Copyright (C) 2019 Yasuhiro Matsumoto <mattn.jp@gmail.com>. +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +// //go:build sqlite_icu || icu // +build sqlite_icu icu + +// Copyright (C) 2019 Yasuhiro Matsumoto <mattn.jp@gmail.com>. +// Copyright (C) 2018 G.J.R. Timmer <gjr.timmer@gmail.com>. + +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +// //go:build sqlite_introspect // +build sqlite_introspect + +// Copyright (C) 2022 Yasuhiro Matsumoto <mattn.jp@gmail.com>. +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +// //go:build sqlite_math_functions // +build sqlite_math_functions + +// Copyright (C) 2022 Yasuhiro Matsumoto <mattn.jp@gmail.com>. +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +// //go:build sqlite_os_trace // +build sqlite_os_trace + +// Copyright (C) 2019 G.J.R. Timmer <gjr.timmer@gmail.com>. +// Copyright (C) 2018 segment.com <friends@segment.com> +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +// //go:build cgo // +build cgo + +// SQLitePreUpdateData represents all of the data available during a +// pre-update hook call. +type SQLitePreUpdateData struct { + Conn *SQLiteConn + Op int + DatabaseName string + TableName string + OldRowID int64 + NewRowID int64 +} +// Copyright (C) 2019 G.J.R. Timmer <gjr.timmer@gmail.com>. +// Copyright (C) 2018 segment.com <friends@segment.com> +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +// //go:build sqlite_preupdate_hook // +build sqlite_preupdate_hook + +/* +// RegisterPreUpdateHook sets the pre-update hook for a connection. +// +// The callback is passed a SQLitePreUpdateData struct with the data for +// the update, as well as methods for fetching copies of impacted data. +// +// If there is an existing preupdate 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) RegisterPreUpdateHook(callback func(SQLitePreUpdateData)) { + if callback == nil { + C.sqlite3_preupdate_hook(c.db, nil, nil) + } else { + C.sqlite3_preupdate_hook(c.db, (*[0]byte)(unsafe.Pointer(C.preUpdateHookTrampoline)), unsafe.Pointer(newHandle(c, callback))) + } +} + +// Depth returns the source path of the write, see sqlite3_preupdate_depth() +func (d *SQLitePreUpdateData) Depth() int { + return int(C.sqlite3_preupdate_depth(d.Conn.db)) +} + +// Count returns the number of columns in the row +func (d *SQLitePreUpdateData) Count() int { + return int(C.sqlite3_preupdate_count(d.Conn.db)) +} + +func (d *SQLitePreUpdateData) row(dest []any, new bool) error { + for i := 0; i < d.Count() && i < len(dest); i++ { + var val *C.sqlite3_value + var src any + + // Initially I tried making this just a function pointer argument, but + // it's absurdly complicated to pass C function pointers. + if new { + C.sqlite3_preupdate_new(d.Conn.db, C.int(i), &val) + } else { + C.sqlite3_preupdate_old(d.Conn.db, C.int(i), &val) + } + + switch C.sqlite3_value_type(val) { + case C.SQLITE_INTEGER: + src = int64(C.sqlite3_value_int64(val)) + case C.SQLITE_FLOAT: + src = float64(C.sqlite3_value_double(val)) + case C.SQLITE_BLOB: + len := C.sqlite3_value_bytes(val) + blobptr := C.sqlite3_value_blob(val) + src = C.GoBytes(blobptr, len) + case C.SQLITE_TEXT: + len := C.sqlite3_value_bytes(val) + cstrptr := unsafe.Pointer(C.sqlite3_value_text(val)) + src = C.GoBytes(cstrptr, len) + case C.SQLITE_NULL: + src = nil + } + + err := convertAssign(&dest[i], src) + if err != nil { + return err + } + } + + return nil +} + +// Old populates dest with the row data to be replaced. This works similar to +// database/sql's Rows.Scan() +func (d *SQLitePreUpdateData) Old(dest ...any) error { + if d.Op == SQLITE_INSERT { + return errors.New("There is no old row for INSERT operations") + } + return d.row(dest, false) +} + +// New populates dest with the replacement row data. This works similar to +// database/sql's Rows.Scan() +func (d *SQLitePreUpdateData) New(dest ...any) error { + if d.Op == SQLITE_DELETE { + return errors.New("There is no new row for DELETE operations") + } + return d.row(dest, true) +} +*/ +// Copyright (C) 2019 G.J.R. Timmer <gjr.timmer@gmail.com>. +// Copyright (C) 2018 segment.com <friends@segment.com> +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +// //go:build !sqlite_preupdate_hook && cgo // +build !sqlite_preupdate_hook,cgo + +// RegisterPreUpdateHook sets the pre-update hook for a connection. +// +// The callback is passed a SQLitePreUpdateData struct with the data for +// the update, as well as methods for fetching copies of impacted data. +// +// If there is an existing preupdate 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) RegisterPreUpdateHook(callback func(SQLitePreUpdateData)) { + // NOOP +} +// Copyright (C) 2019 Yasuhiro Matsumoto <mattn.jp@gmail.com>. +// Copyright (C) 2018 G.J.R. Timmer <gjr.timmer@gmail.com>. +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +// //go:build sqlite_secure_delete // +build sqlite_secure_delete + +// Copyright (C) 2019 Yasuhiro Matsumoto <mattn.jp@gmail.com>. +// Copyright (C) 2018 G.J.R. Timmer <gjr.timmer@gmail.com>. +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +// //go:build sqlite_secure_delete_fast // +build sqlite_secure_delete_fast + +// //go:build !libsqlite3 || sqlite_serialize // +build !libsqlite3 sqlite_serialize + +// Serialize returns a byte slice that is a serialization of the database. +// +// See https://www.sqlite.org/c3ref/serialize.html +func (c *SQLiteConn) Serialize(schema string) ([]byte, error) { + if schema == "" { + schema = "main" + } + var zSchema *C.char + zSchema = C.CString(schema) + defer C.free(unsafe.Pointer(zSchema)) + + var sz C.sqlite3_int64 + ptr := C.sqlite3_serialize(c.db, zSchema, &sz, 0) + if ptr == nil { + return nil, fmt.Errorf("serialize failed") + } + defer C.sqlite3_free(unsafe.Pointer(ptr)) + + if sz > C.sqlite3_int64(math.MaxInt) { + return nil, fmt.Errorf("serialized database is too large (%d bytes)", sz) + } + + cBuf := *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{ + Data: uintptr(unsafe.Pointer(ptr)), + Len: int(sz), + Cap: int(sz), + })) + + res := make([]byte, int(sz)) + copy(res, cBuf) + return res, nil +} + +// Deserialize causes the connection to disconnect from the current database and +// then re-open as an in-memory database based on the contents of the byte slice. +// +// See https://www.sqlite.org/c3ref/deserialize.html +func (c *SQLiteConn) Deserialize(b []byte, schema string) error { + if schema == "" { + schema = "main" + } + var zSchema *C.char + zSchema = C.CString(schema) + defer C.free(unsafe.Pointer(zSchema)) + + tmpBuf := (*C.uchar)(C.sqlite3_malloc64(C.sqlite3_uint64(len(b)))) + cBuf := *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{ + Data: uintptr(unsafe.Pointer(tmpBuf)), + Len: len(b), + Cap: len(b), + })) + copy(cBuf, b) + + rc := C.sqlite3_deserialize(c.db, zSchema, tmpBuf, C.sqlite3_int64(len(b)), + C.sqlite3_int64(len(b)), C.SQLITE_DESERIALIZE_FREEONCLOSE) + if rc != C.SQLITE_OK { + return fmt.Errorf("deserialize failed with return %v", rc) + } + return nil +} +// //go:build libsqlite3 && !sqlite_serialize // +build libsqlite3,!sqlite_serialize + +/* +func (c *SQLiteConn) Serialize(schema string) ([]byte, error) { + return nil, errors.New("sqlite3: Serialize requires the sqlite_serialize build tag when using the libsqlite3 build tag") +} + +func (c *SQLiteConn) Deserialize(b []byte, schema string) error { + return errors.New("sqlite3: Deserialize requires the sqlite_serialize build tag when using the libsqlite3 build tag") +} +*/ +// Copyright (C) 2019 Yasuhiro Matsumoto <mattn.jp@gmail.com>. +// Copyright (C) 2018 G.J.R. Timmer <gjr.timmer@gmail.com>. +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +// //go:build sqlite_stat4 // +build sqlite_stat4 + +// Copyright (C) 2019 Yasuhiro Matsumoto <mattn.jp@gmail.com>. +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +// //go:build cgo && sqlite_unlock_notify // +build cgo,sqlite_unlock_notify + +type unlock_notify_table struct { + sync.Mutex + seqnum uint + table map[uint]chan struct{} +} + +var unt unlock_notify_table = unlock_notify_table{table: make(map[uint]chan struct{})} + +func (t *unlock_notify_table) add(c chan struct{}) uint { + t.Lock() + defer t.Unlock() + h := t.seqnum + t.table[h] = c + t.seqnum++ + return h +} + +func (t *unlock_notify_table) remove(h uint) { + t.Lock() + defer t.Unlock() + delete(t.table, h) +} + +func (t *unlock_notify_table) get(h uint) chan struct{} { + t.Lock() + defer t.Unlock() + c, ok := t.table[h] + if !ok { + panic(fmt.Sprintf("Non-existent key for unlcok-notify channel: %d", h)) + } + 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]) + arg := *parg + h := arg[0] + c := unt.get(h) + c <- struct{}{} + } +} + +//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 +} +// Copyright (C) 2018 G.J.R. Timmer <gjr.timmer@gmail.com>. +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +// //go:build sqlite_userauth // +build sqlite_userauth + +/* +const ( + SQLITE_AUTH = C.SQLITE_AUTH +) + +var ( + ErrUnauthorized = errors.New("SQLITE_AUTH: Unauthorized") + ErrAdminRequired = errors.New("SQLITE_AUTH: Unauthorized; Admin Privileges Required") +) + +// Authenticate will perform an authentication of the provided username +// and password against the database. +// +// If a database contains the SQLITE_USER table, then the +// call to Authenticate must be invoked with an +// appropriate username and password prior to enable read and write +// access to the database. +// +// Return SQLITE_OK on success or SQLITE_ERROR if the username/password +// combination is incorrect or unknown. +// +// If the SQLITE_USER table is not present in the database file, then +// this interface is a harmless no-op returning SQLITE_OK. +func (c *SQLiteConn) Authenticate(username, password string) error { + rv := c.authenticate(username, password) + switch rv { + case C.SQLITE_ERROR, C.SQLITE_AUTH: + return ErrUnauthorized + case C.SQLITE_OK: + return nil + default: + return c.lastError() + } +} + +// authenticate provides the actual authentication to SQLite. +// This is not exported for usage in Go. +// It is however exported for usage within SQL by the user. +// +// Returns: +// +// C.SQLITE_OK (0) +// C.SQLITE_ERROR (1) +// C.SQLITE_AUTH (23) +func (c *SQLiteConn) authenticate(username, password string) int { + // Allocate C Variables + cuser := C.CString(username) + cpass := C.CString(password) + + // Free C Variables + defer func() { + C.free(unsafe.Pointer(cuser)) + C.free(unsafe.Pointer(cpass)) + }() + + return int(C._sqlite3_user_authenticate(c.db, cuser, cpass, C.int(len(password)))) +} + +// AuthUserAdd can be used (by an admin user only) +// to create a new user. When called on a no-authentication-required +// database, this routine converts the database into an authentication- +// required database, automatically makes the added user an +// administrator, and logs in the current connection as that user. +// The AuthUserAdd only works for the "main" database, not +// for any ATTACH-ed databases. Any call to AuthUserAdd by a +// non-admin user results in an error. +func (c *SQLiteConn) AuthUserAdd(username, password string, admin bool) error { + isAdmin := 0 + if admin { + isAdmin = 1 + } + + rv := c.authUserAdd(username, password, isAdmin) + switch rv { + case C.SQLITE_ERROR, C.SQLITE_AUTH: + return ErrAdminRequired + case C.SQLITE_OK: + return nil + default: + return c.lastError() + } +} + +// authUserAdd enables the User Authentication if not enabled. +// Otherwise it will add a user. +// +// When user authentication is already enabled then this function +// can only be called by an admin. +// +// This is not exported for usage in Go. +// It is however exported for usage within SQL by the user. +// +// Returns: +// +// C.SQLITE_OK (0) +// C.SQLITE_ERROR (1) +// C.SQLITE_AUTH (23) +func (c *SQLiteConn) authUserAdd(username, password string, admin int) int { + // Allocate C Variables + cuser := C.CString(username) + cpass := C.CString(password) + + // Free C Variables + defer func() { + C.free(unsafe.Pointer(cuser)) + C.free(unsafe.Pointer(cpass)) + }() + + return int(C._sqlite3_user_add(c.db, cuser, cpass, C.int(len(password)), C.int(admin))) +} + +// AuthUserChange can be used to change a users +// login credentials or admin privilege. Any user can change their own +// login credentials. Only an admin user can change another users login +// credentials or admin privilege setting. No user may change their own +// admin privilege setting. +func (c *SQLiteConn) AuthUserChange(username, password string, admin bool) error { + isAdmin := 0 + if admin { + isAdmin = 1 + } + + rv := c.authUserChange(username, password, isAdmin) + switch rv { + case C.SQLITE_ERROR, C.SQLITE_AUTH: + return ErrAdminRequired + case C.SQLITE_OK: + return nil + default: + return c.lastError() + } +} + +// authUserChange allows to modify a user. +// Users can change their own password. +// +// Only admins can change passwords for other users +// and modify the admin flag. +// +// The admin flag of the current logged in user cannot be changed. +// THis ensures that their is always an admin. +// +// This is not exported for usage in Go. +// It is however exported for usage within SQL by the user. +// +// Returns: +// +// C.SQLITE_OK (0) +// C.SQLITE_ERROR (1) +// C.SQLITE_AUTH (23) +func (c *SQLiteConn) authUserChange(username, password string, admin int) int { + // Allocate C Variables + cuser := C.CString(username) + cpass := C.CString(password) + + // Free C Variables + defer func() { + C.free(unsafe.Pointer(cuser)) + C.free(unsafe.Pointer(cpass)) + }() + + return int(C._sqlite3_user_change(c.db, cuser, cpass, C.int(len(password)), C.int(admin))) +} + +// AuthUserDelete can be used (by an admin user only) +// to delete a user. The currently logged-in user cannot be deleted, +// which guarantees that there is always an admin user and hence that +// the database cannot be converted into a no-authentication-required +// database. +func (c *SQLiteConn) AuthUserDelete(username string) error { + rv := c.authUserDelete(username) + switch rv { + case C.SQLITE_ERROR, C.SQLITE_AUTH: + return ErrAdminRequired + case C.SQLITE_OK: + return nil + default: + return c.lastError() + } +} + +// authUserDelete can be used to delete a user. +// +// This function can only be executed by an admin. +// +// This is not exported for usage in Go. +// It is however exported for usage within SQL by the user. +// +// Returns: +// +// C.SQLITE_OK (0) +// C.SQLITE_ERROR (1) +// C.SQLITE_AUTH (23) +func (c *SQLiteConn) authUserDelete(username string) int { + // Allocate C Variables + cuser := C.CString(username) + + // Free C Variables + defer func() { + C.free(unsafe.Pointer(cuser)) + }() + + return int(C._sqlite3_user_delete(c.db, cuser)) +} + +// AuthEnabled checks if the database is protected by user authentication +func (c *SQLiteConn) AuthEnabled() (exists bool) { + rv := c.authEnabled() + if rv == 1 { + exists = true + } + + return +} + +// authEnabled perform the actual check for user authentication. +// +// This is not exported for usage in Go. +// It is however exported for usage within SQL by the user. +// +// Returns: +// +// 0 - Disabled +// 1 - Enabled +func (c *SQLiteConn) authEnabled() int { + return int(C._sqlite3_auth_enabled(c.db)) +} + +// EOF +// Copyright (C) 2018 G.J.R. Timmer <gjr.timmer@gmail.com>. +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +// //go:build !sqlite_userauth // +build !sqlite_userauth + +// Authenticate will perform an authentication of the provided username +// and password against the database. +// +// If a database contains the SQLITE_USER table, then the +// call to Authenticate must be invoked with an +// appropriate username and password prior to enable read and write +// access to the database. +// +// Return SQLITE_OK on success or SQLITE_ERROR if the username/password +// combination is incorrect or unknown. +// +// If the SQLITE_USER table is not present in the database file, then +// this interface is a harmless no-op returnning SQLITE_OK. +func (c *SQLiteConn) Authenticate(username, password string) error { + // NOOP + return nil +} + +// authenticate provides the actual authentication to SQLite. +// This is not exported for usage in Go. +// It is however exported for usage within SQL by the user. +// +// Returns: +// +// C.SQLITE_OK (0) +// C.SQLITE_ERROR (1) +// C.SQLITE_AUTH (23) +func (c *SQLiteConn) authenticate(username, password string) int { + // NOOP + return 0 +} + +// AuthUserAdd can be used (by an admin user only) +// to create a new user. When called on a no-authentication-required +// database, this routine converts the database into an authentication- +// required database, automatically makes the added user an +// administrator, and logs in the current connection as that user. +// The AuthUserAdd only works for the "main" database, not +// for any ATTACH-ed databases. Any call to AuthUserAdd by a +// non-admin user results in an error. +func (c *SQLiteConn) AuthUserAdd(username, password string, admin bool) error { + // NOOP + return nil +} + +// authUserAdd enables the User Authentication if not enabled. +// Otherwise it will add a user. +// +// When user authentication is already enabled then this function +// can only be called by an admin. +// +// This is not exported for usage in Go. +// It is however exported for usage within SQL by the user. +// +// Returns: +// +// C.SQLITE_OK (0) +// C.SQLITE_ERROR (1) +// C.SQLITE_AUTH (23) +func (c *SQLiteConn) authUserAdd(username, password string, admin int) int { + // NOOP + return 0 +} + +// AuthUserChange can be used to change a users +// login credentials or admin privilege. Any user can change their own +// login credentials. Only an admin user can change another users login +// credentials or admin privilege setting. No user may change their own +// admin privilege setting. +func (c *SQLiteConn) AuthUserChange(username, password string, admin bool) error { + // NOOP + return nil +} + +// authUserChange allows to modify a user. +// Users can change their own password. +// +// Only admins can change passwords for other users +// and modify the admin flag. +// +// The admin flag of the current logged in user cannot be changed. +// THis ensures that their is always an admin. +// +// This is not exported for usage in Go. +// It is however exported for usage within SQL by the user. +// +// Returns: +// +// C.SQLITE_OK (0) +// C.SQLITE_ERROR (1) +// C.SQLITE_AUTH (23) +func (c *SQLiteConn) authUserChange(username, password string, admin int) int { + // NOOP + return 0 +} + +// AuthUserDelete can be used (by an admin user only) +// to delete a user. The currently logged-in user cannot be deleted, +// which guarantees that there is always an admin user and hence that +// the database cannot be converted into a no-authentication-required +// database. +func (c *SQLiteConn) AuthUserDelete(username string) error { + // NOOP + return nil +} + +// authUserDelete can be used to delete a user. +// +// This function can only be executed by an admin. +// +// This is not exported for usage in Go. +// It is however exported for usage within SQL by the user. +// +// Returns: +// +// C.SQLITE_OK (0) +// C.SQLITE_ERROR (1) +// C.SQLITE_AUTH (23) +func (c *SQLiteConn) authUserDelete(username string) int { + // NOOP + return 0 +} + +// AuthEnabled checks if the database is protected by user authentication +func (c *SQLiteConn) AuthEnabled() (exists bool) { + // NOOP + return false +} + +// authEnabled perform the actual check for user authentication. +// +// This is not exported for usage in Go. +// It is however exported for usage within SQL by the user. +// +// Returns: +// +// 0 - Disabled +// 1 - Enabled +func (c *SQLiteConn) authEnabled() int { + // NOOP + return 0 +} +*/ + +// EOF +// Copyright (C) 2019 Yasuhiro Matsumoto <mattn.jp@gmail.com>. +// Copyright (C) 2018 G.J.R. Timmer <gjr.timmer@gmail.com>. +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +// //go:build sqlite_vacuum_full // +build sqlite_vacuum_full + +// Copyright (C) 2019 Yasuhiro Matsumoto <mattn.jp@gmail.com>. +// Copyright (C) 2018 G.J.R. Timmer <gjr.timmer@gmail.com>. +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +// //go:build sqlite_vacuum_incr // +build sqlite_vacuum_incr + +// Copyright (C) 2019 Yasuhiro Matsumoto <mattn.jp@gmail.com>. +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +// //go:build sqlite_vtable || vtable // +build sqlite_vtable vtable + +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 + +// Op mean identity of operations. +const ( + OpEQ Op = 2 + OpGT = 4 + OpLE = 8 + OpLT = 16 + OpGE = 32 + OpMATCH = 64 + OpLIKE = 65 /* 3.10.0 and later only */ + OpGLOB = 66 /* 3.10.0 and later only */ + OpREGEXP = 67 /* 3.10.0 and later only */ + OpScanUnique = 1 /* Scan visits at most 1 row */ +) + +// InfoConstraint give information of constraint. +type InfoConstraint struct { + Column int + Op Op + Usable bool +} + +// InfoOrderBy give information of order-by. +type InfoOrderBy struct { + Column int + Desc bool +} + +func constraints(info *C.sqlite3_index_info) []InfoConstraint { + slice := *(*[]C.struct_sqlite3_index_constraint)(unsafe.Pointer(&reflect.SliceHeader{ + Data: uintptr(unsafe.Pointer(info.aConstraint)), + Len: int(info.nConstraint), + Cap: int(info.nConstraint), + })) + + cst := make([]InfoConstraint, 0, len(slice)) + for _, c := range slice { + var usable bool + if c.usable > 0 { + usable = true + } + cst = append(cst, InfoConstraint{ + Column: int(c.iColumn), + Op: Op(c.op), + Usable: usable, + }) + } + return cst +} + +func orderBys(info *C.sqlite3_index_info) []InfoOrderBy { + slice := *(*[]C.struct_sqlite3_index_orderby)(unsafe.Pointer(&reflect.SliceHeader{ + Data: uintptr(unsafe.Pointer(info.aOrderBy)), + Len: int(info.nOrderBy), + Cap: int(info.nOrderBy), + })) + + ob := make([]InfoOrderBy, 0, len(slice)) + for _, c := range slice { + var desc bool + if c.desc > 0 { + desc = true + } + ob = append(ob, InfoOrderBy{ + Column: int(c.iColumn), + Desc: desc, + }) + } + return ob +} + +// IndexResult is a Go struct representation of what eventually ends up in the +// output fields for `sqlite3_index_info` +// See: https://www.sqlite.org/c3ref/index_info.html +type IndexResult struct { + Used []bool // aConstraintUsage + IdxNum int + IdxStr string + AlreadyOrdered bool // orderByConsumed + EstimatedCost float64 + 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 +} +// Copyright (C) 2019 Yasuhiro Matsumoto <mattn.jp@gmail.com>. +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +// //go:build !windows // +build !windows + +// Copyright (C) 2019 Yasuhiro Matsumoto <mattn.jp@gmail.com>. +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +// //go:build solaris // +build solaris + +// Copyright (C) 2019 Yasuhiro Matsumoto <mattn.jp@gmail.com>. +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +// //go:build sqlite_trace || trace // +build sqlite_trace trace + +// 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)) + dbErr.ExtendedCode = ErrNoExtended(C.sqlite3_extended_errcode(db)) + 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 +} +// Copyright (C) 2019 Yasuhiro Matsumoto <mattn.jp@gmail.com>. +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +// ColumnTypeDatabaseTypeName implement RowsColumnTypeDatabaseTypeName. +func (rc *SQLiteRows) ColumnTypeDatabaseTypeName(i int) string { + return C.GoString(C.sqlite3_column_decltype(rc.s.s, C.int(i))) +} + +/* +func (rc *SQLiteRows) ColumnTypeLength(index int) (length int64, ok bool) { + return 0, false +} + +func (rc *SQLiteRows) ColumnTypePrecisionScale(index int) (precision, scale int64, ok bool) { + return 0, 0, false +} +*/ + +// ColumnTypeNullable implement RowsColumnTypeNullable. +func (rc *SQLiteRows) ColumnTypeNullable(i int) (nullable, ok bool) { + return true, true +} + +// ColumnTypeScanType implement RowsColumnTypeScanType. +func (rc *SQLiteRows) ColumnTypeScanType(i int) reflect.Type { + //ct := C.sqlite3_column_type(rc.s.s, C.int(i)) // Always returns 5 + return scanType(C.GoString(C.sqlite3_column_decltype(rc.s.s, C.int(i)))) +} + +const ( + SQLITE_INTEGER = iota + SQLITE_TEXT + SQLITE_BLOB + SQLITE_REAL + SQLITE_NUMERIC + SQLITE_TIME + SQLITE_BOOL + SQLITE_NULL +) + +func scanType(cdt string) reflect.Type { + t := strings.ToUpper(cdt) + i := databaseTypeConvSqlite(t) + switch i { + case SQLITE_INTEGER: + return reflect.TypeOf(sql.NullInt64{}) + case SQLITE_TEXT: + return reflect.TypeOf(sql.NullString{}) + case SQLITE_BLOB: + return reflect.TypeOf(sql.RawBytes{}) + case SQLITE_REAL: + return reflect.TypeOf(sql.NullFloat64{}) + case SQLITE_NUMERIC: + return reflect.TypeOf(sql.NullFloat64{}) + case SQLITE_BOOL: + return reflect.TypeOf(sql.NullBool{}) + case SQLITE_TIME: + return reflect.TypeOf(sql.NullTime{}) + } + return reflect.TypeOf(new(any)) +} + +func databaseTypeConvSqlite(t string) int { + if strings.Contains(t, "INT") { + return SQLITE_INTEGER + } + if t == "CLOB" || t == "TEXT" || + strings.Contains(t, "CHAR") { + return SQLITE_TEXT + } + if t == "BLOB" { + return SQLITE_BLOB + } + if t == "REAL" || t == "FLOAT" || + strings.Contains(t, "DOUBLE") { + return SQLITE_REAL + } + if t == "DATE" || t == "DATETIME" || + t == "TIMESTAMP" { + return SQLITE_TIME + } + if t == "NUMERIC" || + strings.Contains(t, "DECIMAL") { + return SQLITE_NUMERIC + } + if t == "BOOLEAN" { + return SQLITE_BOOL + } + + return SQLITE_NULL +} +// Copyright (C) 2018 G.J.R. Timmer <gjr.timmer@gmail.com>. +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +// //go:build cgo // +build cgo + +// usleep is a function available on *nix based systems. +// This function is not present in Windows. +// Windows has a sleep function but this works with seconds +// and not with microseconds as usleep. +// +// This code should improve performance on windows because +// without the presence of usleep SQLite waits 1 second. +// +// Source: https://github.com/php/php-src/blob/PHP-5.0/win32/time.c +// License: https://github.com/php/php-src/blob/PHP-5.0/LICENSE +// Details: https://stackoverflow.com/questions/5801813/c-usleep-is-obsolete-workarounds-for-windows-mingw?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa + +// EOF +// Copyright (C) 2019 Yasuhiro Matsumoto <mattn.jp@gmail.com>. +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +// //go:build windows // +build windows + +// Copyright (C) 2019 Yasuhiro Matsumoto <mattn.jp@gmail.com>. +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +// //go:build !cgo // +build !cgo + +var errorMsg = errors.New("Binary was compiled with 'CGO_ENABLED=0', go-sqlite3 requires cgo to work. This is a stub") + + /* +type ( + SQLiteDriver struct { + Extensions []string + ConnectHook func(*SQLiteConn) error + } + SQLiteConn struct{} +) + +func (SQLiteDriver) Open(s string) (driver.Conn, error) { return nil, errorMsg } +func (c *SQLiteConn) RegisterAggregator(string, any, bool) error { return errorMsg } +func (c *SQLiteConn) RegisterAuthorizer(func(int, string, string, string) int) {} +func (c *SQLiteConn) RegisterCollation(string, func(string, string) int) error { return errorMsg } +func (c *SQLiteConn) RegisterCommitHook(func() int) {} +func (c *SQLiteConn) RegisterFunc(string, any, bool) error { return errorMsg } +func (c *SQLiteConn) RegisterRollbackHook(func()) {} +func (c *SQLiteConn) RegisterUpdateHook(func(int, string, string, int64)) {} + */ |