diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-08-24 16:58:43 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-08-24 16:58:43 +0000 |
commit | 08944810f3b9632de38ae92499c63464f212e0fe (patch) | |
tree | 0cc5b41397bbde6e52728e1f862d29d141b1c9cb /tea/generic/tclsqlite3.c | |
parent | 24adc227bc29cd17e39df097fbca389c7724cd14 (diff) | |
download | sqlite3-master.tar.gz |
sqlite-autoconf-3200100HEADsqlite-autoconf-3200100master
Diffstat (limited to 'tea/generic/tclsqlite3.c')
-rw-r--r-- | tea/generic/tclsqlite3.c | 827 |
1 files changed, 624 insertions, 203 deletions
diff --git a/tea/generic/tclsqlite3.c b/tea/generic/tclsqlite3.c index 26b4e47..afa189a 100644 --- a/tea/generic/tclsqlite3.c +++ b/tea/generic/tclsqlite3.c @@ -35,10 +35,17 @@ ** If requested, include the SQLite compiler options file for MSVC. */ #if defined(INCLUDE_MSVC_H) -#include "msvc.h" +# include "msvc.h" #endif -#include "tcl.h" +#if defined(INCLUDE_SQLITE_TCL_H) +# include "sqlite_tcl.h" +#else +# include "tcl.h" +# ifndef SQLITE_TCLAPI +# define SQLITE_TCLAPI +# endif +#endif #include <errno.h> /* @@ -138,6 +145,7 @@ struct SqliteDb { char *zBusy; /* The busy callback routine */ char *zCommit; /* The commit hook callback routine */ char *zTrace; /* The trace callback routine */ + char *zTraceV2; /* The trace_v2 callback routine */ char *zProfile; /* The profile callback routine */ char *zProgress; /* The progress callback routine */ char *zAuth; /* The authorization callback routine */ @@ -145,6 +153,7 @@ struct SqliteDb { char *zNull; /* Text to substitute for an SQL NULL value */ SqlFunc *pFunc; /* List of SQL functions */ Tcl_Obj *pUpdateHook; /* Update hook script (if any) */ + Tcl_Obj *pPreUpdateHook; /* Pre-update hook script (if any) */ Tcl_Obj *pRollbackHook; /* Rollback hook script (if any) */ Tcl_Obj *pWalHook; /* WAL hook script (if any) */ Tcl_Obj *pUnlockNotify; /* Unlock notify script (if any) */ @@ -157,7 +166,9 @@ struct SqliteDb { int nStmt; /* Number of statements in stmtList */ IncrblobChannel *pIncrblob;/* Linked list of open incrblob channels */ int nStep, nSort, nIndex; /* Statistics for most recent operation */ + int nVMStep; /* Another statistic for most recent operation */ int nTransaction; /* Number of nested [transaction] methods */ + int openFlags; /* Flags used to open. (SQLITE_OPEN_URI) */ #ifdef SQLITE_TEST int bLegacyPrepare; /* True to use sqlite3_prepare() */ #endif @@ -195,7 +206,7 @@ static void closeIncrblobChannels(SqliteDb *pDb){ for(p=pDb->pIncrblob; p; p=pNext){ pNext = p->pNext; - /* Note: Calling unregister here call Tcl_Close on the incrblob channel, + /* Note: Calling unregister here call Tcl_Close on the incrblob channel, ** which deletes the IncrblobChannel structure at *p. So do not ** call Tcl_Free() here. */ @@ -206,7 +217,10 @@ static void closeIncrblobChannels(SqliteDb *pDb){ /* ** Close an incremental blob channel. */ -static int incrblobClose(ClientData instanceData, Tcl_Interp *interp){ +static int SQLITE_TCLAPI incrblobClose( + ClientData instanceData, + Tcl_Interp *interp +){ IncrblobChannel *p = (IncrblobChannel *)instanceData; int rc = sqlite3_blob_close(p->pBlob); sqlite3 *db = p->pDb->db; @@ -235,9 +249,9 @@ static int incrblobClose(ClientData instanceData, Tcl_Interp *interp){ /* ** Read data from an incremental blob channel. */ -static int incrblobInput( - ClientData instanceData, - char *buf, +static int SQLITE_TCLAPI incrblobInput( + ClientData instanceData, + char *buf, int bufSize, int *errorCodePtr ){ @@ -267,9 +281,9 @@ static int incrblobInput( /* ** Write data to an incremental blob channel. */ -static int incrblobOutput( - ClientData instanceData, - CONST char *buf, +static int SQLITE_TCLAPI incrblobOutput( + ClientData instanceData, + CONST char *buf, int toWrite, int *errorCodePtr ){ @@ -300,8 +314,8 @@ static int incrblobOutput( /* ** Seek an incremental blob channel. */ -static int incrblobSeek( - ClientData instanceData, +static int SQLITE_TCLAPI incrblobSeek( + ClientData instanceData, long offset, int seekMode, int *errorCodePtr @@ -326,10 +340,17 @@ static int incrblobSeek( } -static void incrblobWatch(ClientData instanceData, int mode){ - /* NO-OP */ +static void SQLITE_TCLAPI incrblobWatch( + ClientData instanceData, + int mode +){ + /* NO-OP */ } -static int incrblobHandle(ClientData instanceData, int dir, ClientData *hPtr){ +static int SQLITE_TCLAPI incrblobHandle( + ClientData instanceData, + int dir, + ClientData *hPtr +){ return TCL_ERROR; } @@ -355,11 +376,11 @@ static Tcl_ChannelType IncrblobChannelType = { ** Create a new incrblob channel. */ static int createIncrblobChannel( - Tcl_Interp *interp, - SqliteDb *pDb, + Tcl_Interp *interp, + SqliteDb *pDb, const char *zDb, - const char *zTable, - const char *zColumn, + const char *zTable, + const char *zColumn, sqlite_int64 iRow, int isReadonly ){ @@ -441,7 +462,7 @@ static SqlFunc *findSqlFunc(SqliteDb *pDb, const char *zName){ pNew = (SqlFunc*)Tcl_Alloc( sizeof(*pNew) + nName + 1 ); pNew->zName = (char*)&pNew[1]; memcpy(pNew->zName, zName, nName+1); - for(p=pDb->pFunc; p; p=p->pNext){ + for(p=pDb->pFunc; p; p=p->pNext){ if( sqlite3_stricmp(p->zName, pNew->zName)==0 ){ Tcl_Free((char*)pNew); return p; @@ -488,7 +509,7 @@ static void flushStmtCache(SqliteDb *pDb){ ** TCL calls this procedure when an sqlite3 database command is ** deleted. */ -static void DbDeleteCmd(void *db){ +static void SQLITE_TCLAPI DbDeleteCmd(void *db){ SqliteDb *pDb = (SqliteDb*)db; flushStmtCache(pDb); closeIncrblobChannels(pDb); @@ -511,6 +532,9 @@ static void DbDeleteCmd(void *db){ if( pDb->zTrace ){ Tcl_Free(pDb->zTrace); } + if( pDb->zTraceV2 ){ + Tcl_Free(pDb->zTraceV2); + } if( pDb->zProfile ){ Tcl_Free(pDb->zProfile); } @@ -523,6 +547,9 @@ static void DbDeleteCmd(void *db){ if( pDb->pUpdateHook ){ Tcl_DecrRefCount(pDb->pUpdateHook); } + if( pDb->pPreUpdateHook ){ + Tcl_DecrRefCount(pDb->pPreUpdateHook); + } if( pDb->pRollbackHook ){ Tcl_DecrRefCount(pDb->pRollbackHook); } @@ -569,7 +596,8 @@ static int DbProgressHandler(void *cd){ } #endif -#ifndef SQLITE_OMIT_TRACE +#if !defined(SQLITE_OMIT_TRACE) && !defined(SQLITE_OMIT_FLOATING_POINT) && \ + !defined(SQLITE_OMIT_DEPRECATED) /* ** This routine is called by the SQLite trace handler whenever a new ** block of SQL is executed. The TCL script in pDb->zTrace is executed. @@ -589,6 +617,83 @@ static void DbTraceHandler(void *cd, const char *zSql){ #ifndef SQLITE_OMIT_TRACE /* +** This routine is called by the SQLite trace_v2 handler whenever a new +** supported event is generated. Unsupported event types are ignored. +** The TCL script in pDb->zTraceV2 is executed, with the arguments for +** the event appended to it (as list elements). +*/ +static int DbTraceV2Handler( + unsigned type, /* One of the SQLITE_TRACE_* event types. */ + void *cd, /* The original context data pointer. */ + void *pd, /* Primary event data, depends on event type. */ + void *xd /* Extra event data, depends on event type. */ +){ + SqliteDb *pDb = (SqliteDb*)cd; + Tcl_Obj *pCmd; + + switch( type ){ + case SQLITE_TRACE_STMT: { + sqlite3_stmt *pStmt = (sqlite3_stmt *)pd; + char *zSql = (char *)xd; + + pCmd = Tcl_NewStringObj(pDb->zTraceV2, -1); + Tcl_IncrRefCount(pCmd); + Tcl_ListObjAppendElement(pDb->interp, pCmd, + Tcl_NewWideIntObj((Tcl_WideInt)pStmt)); + Tcl_ListObjAppendElement(pDb->interp, pCmd, + Tcl_NewStringObj(zSql, -1)); + Tcl_EvalObjEx(pDb->interp, pCmd, TCL_EVAL_DIRECT); + Tcl_DecrRefCount(pCmd); + Tcl_ResetResult(pDb->interp); + break; + } + case SQLITE_TRACE_PROFILE: { + sqlite3_stmt *pStmt = (sqlite3_stmt *)pd; + sqlite3_int64 ns = (sqlite3_int64)xd; + + pCmd = Tcl_NewStringObj(pDb->zTraceV2, -1); + Tcl_IncrRefCount(pCmd); + Tcl_ListObjAppendElement(pDb->interp, pCmd, + Tcl_NewWideIntObj((Tcl_WideInt)pStmt)); + Tcl_ListObjAppendElement(pDb->interp, pCmd, + Tcl_NewWideIntObj((Tcl_WideInt)ns)); + Tcl_EvalObjEx(pDb->interp, pCmd, TCL_EVAL_DIRECT); + Tcl_DecrRefCount(pCmd); + Tcl_ResetResult(pDb->interp); + break; + } + case SQLITE_TRACE_ROW: { + sqlite3_stmt *pStmt = (sqlite3_stmt *)pd; + + pCmd = Tcl_NewStringObj(pDb->zTraceV2, -1); + Tcl_IncrRefCount(pCmd); + Tcl_ListObjAppendElement(pDb->interp, pCmd, + Tcl_NewWideIntObj((Tcl_WideInt)pStmt)); + Tcl_EvalObjEx(pDb->interp, pCmd, TCL_EVAL_DIRECT); + Tcl_DecrRefCount(pCmd); + Tcl_ResetResult(pDb->interp); + break; + } + case SQLITE_TRACE_CLOSE: { + sqlite3 *db = (sqlite3 *)pd; + + pCmd = Tcl_NewStringObj(pDb->zTraceV2, -1); + Tcl_IncrRefCount(pCmd); + Tcl_ListObjAppendElement(pDb->interp, pCmd, + Tcl_NewWideIntObj((Tcl_WideInt)db)); + Tcl_EvalObjEx(pDb->interp, pCmd, TCL_EVAL_DIRECT); + Tcl_DecrRefCount(pCmd); + Tcl_ResetResult(pDb->interp); + break; + } + } + return SQLITE_OK; +} +#endif + +#if !defined(SQLITE_OMIT_TRACE) && !defined(SQLITE_OMIT_FLOATING_POINT) && \ + !defined(SQLITE_OMIT_DEPRECATED) +/* ** This routine is called by the SQLite profile handler after a statement ** SQL has executed. The TCL script in pDb->zProfile is evaluated. */ @@ -637,9 +742,9 @@ static void DbRollbackHandler(void *clientData){ ** This procedure handles wal_hook callbacks. */ static int DbWalHandler( - void *clientData, - sqlite3 *db, - const char *zDb, + void *clientData, + sqlite3 *db, + const char *zDb, int nEntry ){ int ret = SQLITE_OK; @@ -653,7 +758,7 @@ static int DbWalHandler( Tcl_IncrRefCount(p); Tcl_ListObjAppendElement(interp, p, Tcl_NewStringObj(zDb, -1)); Tcl_ListObjAppendElement(interp, p, Tcl_NewIntObj(nEntry)); - if( TCL_OK!=Tcl_EvalObjEx(interp, p, 0) + if( TCL_OK!=Tcl_EvalObjEx(interp, p, 0) || TCL_OK!=Tcl_GetIntFromObj(interp, Tcl_GetObjResult(interp), &ret) ){ Tcl_BackgroundError(interp); @@ -666,9 +771,9 @@ static int DbWalHandler( #if defined(SQLITE_TEST) && defined(SQLITE_ENABLE_UNLOCK_NOTIFY) static void setTestUnlockNotifyVars(Tcl_Interp *interp, int iArg, int nArg){ char zBuf[64]; - sprintf(zBuf, "%d", iArg); + sqlite3_snprintf(sizeof(zBuf), zBuf, "%d", iArg); Tcl_SetVar(interp, "sqlite_unlock_notify_arg", zBuf, TCL_GLOBAL_ONLY); - sprintf(zBuf, "%d", nArg); + sqlite3_snprintf(sizeof(zBuf), zBuf, "%d", nArg); Tcl_SetVar(interp, "sqlite_unlock_notify_argcount", zBuf, TCL_GLOBAL_ONLY); } #else @@ -690,23 +795,63 @@ static void DbUnlockNotify(void **apArg, int nArg){ } #endif +#ifdef SQLITE_ENABLE_PREUPDATE_HOOK +/* +** Pre-update hook callback. +*/ +static void DbPreUpdateHandler( + void *p, + sqlite3 *db, + int op, + const char *zDb, + const char *zTbl, + sqlite_int64 iKey1, + sqlite_int64 iKey2 +){ + SqliteDb *pDb = (SqliteDb *)p; + Tcl_Obj *pCmd; + static const char *azStr[] = {"DELETE", "INSERT", "UPDATE"}; + + assert( (SQLITE_DELETE-1)/9 == 0 ); + assert( (SQLITE_INSERT-1)/9 == 1 ); + assert( (SQLITE_UPDATE-1)/9 == 2 ); + assert( pDb->pPreUpdateHook ); + assert( db==pDb->db ); + assert( op==SQLITE_INSERT || op==SQLITE_UPDATE || op==SQLITE_DELETE ); + + pCmd = Tcl_DuplicateObj(pDb->pPreUpdateHook); + Tcl_IncrRefCount(pCmd); + Tcl_ListObjAppendElement(0, pCmd, Tcl_NewStringObj(azStr[(op-1)/9], -1)); + Tcl_ListObjAppendElement(0, pCmd, Tcl_NewStringObj(zDb, -1)); + Tcl_ListObjAppendElement(0, pCmd, Tcl_NewStringObj(zTbl, -1)); + Tcl_ListObjAppendElement(0, pCmd, Tcl_NewWideIntObj(iKey1)); + Tcl_ListObjAppendElement(0, pCmd, Tcl_NewWideIntObj(iKey2)); + Tcl_EvalObjEx(pDb->interp, pCmd, TCL_EVAL_DIRECT); + Tcl_DecrRefCount(pCmd); +} +#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ + static void DbUpdateHandler( - void *p, + void *p, int op, - const char *zDb, - const char *zTbl, + const char *zDb, + const char *zTbl, sqlite_int64 rowid ){ SqliteDb *pDb = (SqliteDb *)p; Tcl_Obj *pCmd; + static const char *azStr[] = {"DELETE", "INSERT", "UPDATE"}; + + assert( (SQLITE_DELETE-1)/9 == 0 ); + assert( (SQLITE_INSERT-1)/9 == 1 ); + assert( (SQLITE_UPDATE-1)/9 == 2 ); assert( pDb->pUpdateHook ); assert( op==SQLITE_INSERT || op==SQLITE_UPDATE || op==SQLITE_DELETE ); pCmd = Tcl_DuplicateObj(pDb->pUpdateHook); Tcl_IncrRefCount(pCmd); - Tcl_ListObjAppendElement(0, pCmd, Tcl_NewStringObj( - ( (op==SQLITE_INSERT)?"INSERT":(op==SQLITE_UPDATE)?"UPDATE":"DELETE"), -1)); + Tcl_ListObjAppendElement(0, pCmd, Tcl_NewStringObj(azStr[(op-1)/9], -1)); Tcl_ListObjAppendElement(0, pCmd, Tcl_NewStringObj(zDb, -1)); Tcl_ListObjAppendElement(0, pCmd, Tcl_NewStringObj(zTbl, -1)); Tcl_ListObjAppendElement(0, pCmd, Tcl_NewWideIntObj(rowid)); @@ -775,7 +920,7 @@ static void tclSqlFunc(sqlite3_context *context, int argc, sqlite3_value**argv){ ** script object, lappend the arguments, then evaluate the copy. ** ** By "shallow" copy, we mean only the outer list Tcl_Obj is duplicated. - ** The new Tcl_Obj contains pointers to the original list elements. + ** The new Tcl_Obj contains pointers to the original list elements. ** That way, when Tcl_EvalObjv() is run and shimmers the first element ** of the list to tclCmdNameType, that alternate representation will ** be preserved and reused on the next invocation. @@ -783,15 +928,15 @@ static void tclSqlFunc(sqlite3_context *context, int argc, sqlite3_value**argv){ Tcl_Obj **aArg; int nArg; if( Tcl_ListObjGetElements(p->interp, p->pScript, &nArg, &aArg) ){ - sqlite3_result_error(context, Tcl_GetStringResult(p->interp), -1); + sqlite3_result_error(context, Tcl_GetStringResult(p->interp), -1); return; - } + } pCmd = Tcl_NewListObj(nArg, aArg); Tcl_IncrRefCount(pCmd); for(i=0; i<argc; i++){ sqlite3_value *pIn = argv[i]; Tcl_Obj *pVal; - + /* Set pVal to contain the i'th column of this row. */ switch( sqlite3_value_type(pIn) ){ case SQLITE_BLOB: { @@ -826,7 +971,7 @@ static void tclSqlFunc(sqlite3_context *context, int argc, sqlite3_value**argv){ rc = Tcl_ListObjAppendElement(p->interp, pCmd, pVal); if( rc ){ Tcl_DecrRefCount(pCmd); - sqlite3_result_error(context, Tcl_GetStringResult(p->interp), -1); + sqlite3_result_error(context, Tcl_GetStringResult(p->interp), -1); return; } } @@ -841,7 +986,7 @@ static void tclSqlFunc(sqlite3_context *context, int argc, sqlite3_value**argv){ } if( rc && rc!=TCL_RETURN ){ - sqlite3_result_error(context, Tcl_GetStringResult(p->interp), -1); + sqlite3_result_error(context, Tcl_GetStringResult(p->interp), -1); }else{ Tcl_Obj *pVar = Tcl_GetObjResult(p->interp); int n; @@ -894,9 +1039,16 @@ static int auth_callback( Tcl_DString str; int rc; const char *zReply; + /* EVIDENCE-OF: R-38590-62769 The first parameter to the authorizer + ** callback is a copy of the third parameter to the + ** sqlite3_set_authorizer() interface. + */ SqliteDb *pDb = (SqliteDb*)pArg; if( pDb->disableAuth ) return SQLITE_OK; + /* EVIDENCE-OF: R-56518-44310 The second parameter to the callback is an + ** integer action code that specifies the particular action to be + ** authorized. */ switch( code ){ case SQLITE_COPY : zCode="SQLITE_COPY"; break; case SQLITE_CREATE_INDEX : zCode="SQLITE_CREATE_INDEX"; break; @@ -943,7 +1095,7 @@ static int auth_callback( Tcl_DStringAppendElement(&str, zArg4 ? zArg4 : ""); #ifdef SQLITE_USER_AUTHENTICATION Tcl_DStringAppendElement(&str, zArg5 ? zArg5 : ""); -#endif +#endif rc = Tcl_GlobalEval(pDb->interp, Tcl_DStringValue(&str)); Tcl_DStringFree(&str); zReply = rc==TCL_OK ? Tcl_GetStringResult(pDb->interp) : "SQLITE_DENY"; @@ -1014,7 +1166,7 @@ static char *local_getline(char *zPrompt, FILE *in){ ** It is invoked after evaluating the script SCRIPT to commit or rollback ** the transaction or savepoint opened by the [transaction] command. */ -static int DbTransPostCmd( +static int SQLITE_TCLAPI DbTransPostCmd( ClientData data[], /* data[0] is the Sqlite3Db* for $db */ Tcl_Interp *interp, /* Tcl interpreter */ int result /* Result of evaluating SCRIPT */ @@ -1035,12 +1187,12 @@ static int DbTransPostCmd( pDb->disableAuth++; if( sqlite3_exec(pDb->db, zEnd, 0, 0, 0) ){ /* This is a tricky scenario to handle. The most likely cause of an - ** error is that the exec() above was an attempt to commit the + ** error is that the exec() above was an attempt to commit the ** top-level transaction that returned SQLITE_BUSY. Or, less likely, ** that an IO-error has occurred. In either case, throw a Tcl exception ** and try to rollback the transaction. ** - ** But it could also be that the user executed one or more BEGIN, + ** But it could also be that the user executed one or more BEGIN, ** COMMIT, SAVEPOINT, RELEASE or ROLLBACK commands that are confusing ** this method's logic. Not clear how this would be best handled. */ @@ -1059,7 +1211,7 @@ static int DbTransPostCmd( ** Unless SQLITE_TEST is defined, this function is a simple wrapper around ** sqlite3_prepare_v2(). If SQLITE_TEST is defined, then it uses either ** sqlite3_prepare_v2() or legacy interface sqlite3_prepare(), depending -** on whether or not the [db_use_legacy_prepare] command has been used to +** on whether or not the [db_use_legacy_prepare] command has been used to ** configure the connection. */ static int dbPrepare( @@ -1068,12 +1220,18 @@ static int dbPrepare( sqlite3_stmt **ppStmt, /* OUT: Prepared statement */ const char **pzOut /* OUT: Pointer to next SQL statement */ ){ + unsigned int prepFlags = 0; #ifdef SQLITE_TEST if( pDb->bLegacyPrepare ){ return sqlite3_prepare(pDb->db, zSql, -1, ppStmt, pzOut); } #endif - return sqlite3_prepare_v2(pDb->db, zSql, -1, ppStmt, pzOut); + /* If the statement cache is large, use the SQLITE_PREPARE_PERSISTENT + ** flags, which uses less lookaside memory. But if the cache is small, + ** omit that flag to make full use of lookaside */ + if( pDb->maxStmt>5 ) prepFlags = SQLITE_PREPARE_PERSISTENT; + + return sqlite3_prepare_v3(pDb->db, zSql, -1, prepFlags, ppStmt, pzOut); } /* @@ -1115,7 +1273,7 @@ static int dbPrepareAndBind( for(pPreStmt = pDb->stmtList; pPreStmt; pPreStmt=pPreStmt->pNext){ int n = pPreStmt->nSql; - if( nSql>=n + if( nSql>=n && memcmp(pPreStmt->zSql, zSql, n)==0 && (zSql[n]==0 || zSql[n-1]==';') ){ @@ -1141,7 +1299,7 @@ static int dbPrepareAndBind( break; } } - + /* If no prepared statement was found. Compile the SQL text. Also allocate ** a new SqlPreparedStmt structure. */ if( pPreStmt==0 ){ @@ -1187,7 +1345,7 @@ static int dbPrepareAndBind( assert( strlen30(pPreStmt->zSql)==pPreStmt->nSql ); assert( 0==memcmp(pPreStmt->zSql, zSql, pPreStmt->nSql) ); - /* Bind values to parameters that begin with $ or : */ + /* Bind values to parameters that begin with $ or : */ for(i=1; i<=nVar; i++){ const char *zVar = sqlite3_bind_parameter_name(pStmt, i); if( zVar!=0 && (zVar[0]=='$' || zVar[0]==':' || zVar[0]=='@') ){ @@ -1196,7 +1354,7 @@ static int dbPrepareAndBind( int n; u8 *data; const char *zType = (pVar->typePtr ? pVar->typePtr->name : ""); - char c = zType[0]; + c = zType[0]; if( zVar[0]=='@' || (c=='b' && strcmp(zType,"bytearray")==0 && pVar->bytes==0) ){ /* Load a BLOB type if the Tcl variable is a bytearray and @@ -1275,8 +1433,8 @@ static void dbReleaseStmt( assert( pDb->nStmt>0 ); } pDb->nStmt++; - - /* If we have too many statement in cache, remove the surplus from + + /* If we have too many statement in cache, remove the surplus from ** the end of the cache list. */ while( pDb->nStmt>pDb->maxStmt ){ SqlPreparedStmt *pLast = pDb->stmtLast; @@ -1304,10 +1462,13 @@ struct DbEvalContext { const char *zSql; /* Remaining SQL to execute */ SqlPreparedStmt *pPreStmt; /* Current statement */ int nCol; /* Number of columns returned by pStmt */ + int evalFlags; /* Flags used */ Tcl_Obj *pArray; /* Name of array variable */ Tcl_Obj **apColName; /* Array of column names */ }; +#define SQLITE_EVAL_WITHOUTNULLS 0x00001 /* Unset array(*) for NULL */ + /* ** Release any cache of column names currently held as part of ** the DbEvalContext structure passed as the first argument. @@ -1330,8 +1491,8 @@ static void dbReleaseColumnNames(DbEvalContext *p){ ** If pArray is not NULL, then it contains the name of a Tcl array ** variable. The "*" member of this array is set to a list containing ** the names of the columns returned by the statement as part of each -** call to dbEvalStep(), in order from left to right. e.g. if the names -** of the returned columns are a, b and c, it does the equivalent of the +** call to dbEvalStep(), in order from left to right. e.g. if the names +** of the returned columns are a, b and c, it does the equivalent of the ** tcl command: ** ** set ${pArray}(*) {a b c} @@ -1340,7 +1501,8 @@ static void dbEvalInit( DbEvalContext *p, /* Pointer to structure to initialize */ SqliteDb *pDb, /* Database handle */ Tcl_Obj *pSql, /* Object containing SQL script */ - Tcl_Obj *pArray /* Name of Tcl array to set (*) element of */ + Tcl_Obj *pArray, /* Name of Tcl array to set (*) element of */ + int evalFlags /* Flags controlling evaluation */ ){ memset(p, 0, sizeof(DbEvalContext)); p->pDb = pDb; @@ -1351,6 +1513,7 @@ static void dbEvalInit( p->pArray = pArray; Tcl_IncrRefCount(pArray); } + p->evalFlags = evalFlags; } /* @@ -1442,6 +1605,7 @@ static int dbEvalStep(DbEvalContext *p){ pDb->nStep = sqlite3_stmt_status(pStmt,SQLITE_STMTSTATUS_FULLSCAN_STEP,1); pDb->nSort = sqlite3_stmt_status(pStmt,SQLITE_STMTSTATUS_SORT,1); pDb->nIndex = sqlite3_stmt_status(pStmt,SQLITE_STMTSTATUS_AUTOINDEX,1); + pDb->nVMStep = sqlite3_stmt_status(pStmt,SQLITE_STMTSTATUS_VM_STEP,1); dbReleaseColumnNames(p); p->pPreStmt = 0; @@ -1452,7 +1616,7 @@ static int dbEvalStep(DbEvalContext *p){ #if SQLITE_TEST if( p->pDb->bLegacyPrepare && rcs==SQLITE_SCHEMA && zPrevSql ){ /* If the runtime error was an SQLITE_SCHEMA, and the database - ** handle is configured to use the legacy sqlite3_prepare() + ** handle is configured to use the legacy sqlite3_prepare() ** interface, retry prepare()/step() on the same SQL statement. ** This only happens once. If there is a second SQLITE_SCHEMA ** error, the error will be returned to the caller. */ @@ -1540,11 +1704,11 @@ static int DbUseNre(void){ return( (major==8 && minor>=6) || major>8 ); } #else -/* +/* ** Compiling using headers earlier than 8.6. In this case NR cannot be ** used, so DbUseNre() to always return zero. Add #defines for the other ** Tcl_NRxxx() functions to prevent them from causing compilation errors, -** even though the only invocations of them are within conditional blocks +** even though the only invocations of them are within conditional blocks ** of the form: ** ** if( DbUseNre() ) { ... } @@ -1561,7 +1725,7 @@ static int DbUseNre(void){ ** ** $db eval SQL ?ARRAYNAME? SCRIPT */ -static int DbEvalNextCmd( +static int SQLITE_TCLAPI DbEvalNextCmd( ClientData data[], /* data[0] is the (DbEvalContext*) */ Tcl_Interp *interp, /* Tcl interpreter */ int result /* Result so far */ @@ -1582,19 +1746,23 @@ static int DbEvalNextCmd( Tcl_Obj **apColName; dbEvalRowInfo(p, &nCol, &apColName); for(i=0; i<nCol; i++){ - Tcl_Obj *pVal = dbEvalColumnValue(p, i); if( pArray==0 ){ - Tcl_ObjSetVar2(interp, apColName[i], 0, pVal, 0); + Tcl_ObjSetVar2(interp, apColName[i], 0, dbEvalColumnValue(p,i), 0); + }else if( (p->evalFlags & SQLITE_EVAL_WITHOUTNULLS)!=0 + && sqlite3_column_type(p->pPreStmt->pStmt, i)==SQLITE_NULL + ){ + Tcl_UnsetVar2(interp, Tcl_GetString(pArray), + Tcl_GetString(apColName[i]), 0); }else{ - Tcl_ObjSetVar2(interp, pArray, apColName[i], pVal, 0); + Tcl_ObjSetVar2(interp, pArray, apColName[i], dbEvalColumnValue(p,i), 0); } } - /* The required interpreter variables are now populated with the data + /* The required interpreter variables are now populated with the data ** from the current row. If using NRE, schedule callbacks to evaluate ** script pScript, then to invoke this function again to fetch the next ** row (or clean up if there is no next row or the script throws an - ** exception). After scheduling the callbacks, return control to the + ** exception). After scheduling the callbacks, return control to the ** caller. ** ** If not using NRE, evaluate pScript directly and continue with the @@ -1619,6 +1787,46 @@ static int DbEvalNextCmd( } /* +** This function is used by the implementations of the following database +** handle sub-commands: +** +** $db update_hook ?SCRIPT? +** $db wal_hook ?SCRIPT? +** $db commit_hook ?SCRIPT? +** $db preupdate hook ?SCRIPT? +*/ +static void DbHookCmd( + Tcl_Interp *interp, /* Tcl interpreter */ + SqliteDb *pDb, /* Database handle */ + Tcl_Obj *pArg, /* SCRIPT argument (or NULL) */ + Tcl_Obj **ppHook /* Pointer to member of SqliteDb */ +){ + sqlite3 *db = pDb->db; + + if( *ppHook ){ + Tcl_SetObjResult(interp, *ppHook); + if( pArg ){ + Tcl_DecrRefCount(*ppHook); + *ppHook = 0; + } + } + if( pArg ){ + assert( !(*ppHook) ); + if( Tcl_GetCharLength(pArg)>0 ){ + *ppHook = pArg; + Tcl_IncrRefCount(*ppHook); + } + } + +#ifdef SQLITE_ENABLE_PREUPDATE_HOOK + sqlite3_preupdate_hook(db, (pDb->pPreUpdateHook?DbPreUpdateHandler:0), pDb); +#endif + sqlite3_update_hook(db, (pDb->pUpdateHook?DbUpdateHandler:0), pDb); + sqlite3_rollback_hook(db, (pDb->pRollbackHook?DbRollbackHandler:0), pDb); + sqlite3_wal_hook(db, (pDb->pWalHook?DbWalHandler:0), pDb); +} + +/* ** The "sqlite" command below creates a new Tcl command for each ** connection it opens to an SQLite database. This routine is invoked ** whenever one of those connection-specific commands is executed @@ -1631,7 +1839,12 @@ static int DbEvalNextCmd( ** and calls that connection "db1". The second command causes this ** subroutine to be invoked. */ -static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ +static int SQLITE_TCLAPI DbObjCmd( + void *cd, + Tcl_Interp *interp, + int objc, + Tcl_Obj *const*objv +){ SqliteDb *pDb = (SqliteDb*)cd; int choice; int rc = TCL_OK; @@ -1643,11 +1856,13 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ "errorcode", "eval", "exists", "function", "incrblob", "interrupt", "last_insert_rowid", "nullvalue", "onecolumn", - "profile", "progress", "rekey", - "restore", "rollback_hook", "status", - "timeout", "total_changes", "trace", - "transaction", "unlock_notify", "update_hook", - "version", "wal_hook", 0 + "preupdate", "profile", "progress", + "rekey", "restore", "rollback_hook", + "status", "timeout", "total_changes", + "trace", "trace_v2", "transaction", + "unlock_notify", "update_hook", "version", + "wal_hook", + 0 }; enum DB_enum { DB_AUTHORIZER, DB_BACKUP, DB_BUSY, @@ -1657,11 +1872,12 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ DB_ERRORCODE, DB_EVAL, DB_EXISTS, DB_FUNCTION, DB_INCRBLOB, DB_INTERRUPT, DB_LAST_INSERT_ROWID, DB_NULLVALUE, DB_ONECOLUMN, - DB_PROFILE, DB_PROGRESS, DB_REKEY, - DB_RESTORE, DB_ROLLBACK_HOOK, DB_STATUS, - DB_TIMEOUT, DB_TOTAL_CHANGES, DB_TRACE, - DB_TRANSACTION, DB_UNLOCK_NOTIFY, DB_UPDATE_HOOK, - DB_VERSION, DB_WAL_HOOK + DB_PREUPDATE, DB_PROFILE, DB_PROGRESS, + DB_REKEY, DB_RESTORE, DB_ROLLBACK_HOOK, + DB_STATUS, DB_TIMEOUT, DB_TOTAL_CHANGES, + DB_TRACE, DB_TRACE_V2, DB_TRANSACTION, + DB_UNLOCK_NOTIFY, DB_UPDATE_HOOK, DB_VERSION, + DB_WAL_HOOK, }; /* don't leave trailing commas on DB_enum, it confuses the AIX xlc compiler */ @@ -1755,7 +1971,8 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ Tcl_WrongNumArgs(interp, 2, objv, "?DATABASE? FILENAME"); return TCL_ERROR; } - rc = sqlite3_open(zDestFile, &pDest); + rc = sqlite3_open_v2(zDestFile, &pDest, + SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE| pDb->openFlags, 0); if( rc!=SQLITE_OK ){ Tcl_AppendResult(interp, "cannot open target database: ", sqlite3_errmsg(pDest), (char*)0); @@ -1846,7 +2063,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ return TCL_ERROR; }else{ if( TCL_ERROR==Tcl_GetIntFromObj(interp, objv[3], &n) ){ - Tcl_AppendResult( interp, "cannot convert \"", + Tcl_AppendResult( interp, "cannot convert \"", Tcl_GetStringFromObj(objv[3],0), "\" to integer", (char*)0); return TCL_ERROR; }else{ @@ -1860,7 +2077,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ } } }else{ - Tcl_AppendResult( interp, "bad option \"", + Tcl_AppendResult( interp, "bad option \"", Tcl_GetStringFromObj(objv[2],0), "\": must be flush or size", (char*)0); return TCL_ERROR; @@ -1871,7 +2088,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ /* $db changes ** ** Return the number of rows that were modified, inserted, or deleted by - ** the most recent INSERT, UPDATE or DELETE statement, not including + ** the most recent INSERT, UPDATE or DELETE statement, not including ** any changes made by trigger programs. */ case DB_CHANGES: { @@ -1918,7 +2135,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ pCollate->zScript = (char*)&pCollate[1]; pDb->pCollate = pCollate; memcpy(pCollate->zScript, zScript, nScript+1); - if( sqlite3_create_collation(pDb->db, zName, SQLITE_UTF8, + if( sqlite3_create_collation(pDb->db, zName, SQLITE_UTF8, pCollate, tclSqlCollate) ){ Tcl_SetResult(interp, (char *)sqlite3_errmsg(pDb->db), TCL_VOLATILE); return TCL_ERROR; @@ -2044,7 +2261,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ const char *zSep; const char *zNull; if( objc<5 || objc>7 ){ - Tcl_WrongNumArgs(interp, 2, objv, + Tcl_WrongNumArgs(interp, 2, objv, "CONFLICT-ALGORITHM TABLE FILENAME ?SEPARATOR? ?NULLINDICATOR?"); return TCL_ERROR; } @@ -2073,7 +2290,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ strcmp(zConflict, "fail" ) != 0 && strcmp(zConflict, "ignore" ) != 0 && strcmp(zConflict, "replace" ) != 0 ) { - Tcl_AppendResult(interp, "Error: \"", zConflict, + Tcl_AppendResult(interp, "Error: \"", zConflict, "\", conflict-algorithm must be one of: rollback, " "abort, fail, ignore, or replace", (char*)0); return TCL_ERROR; @@ -2119,7 +2336,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ } in = fopen(zFile, "rb"); if( in==0 ){ - Tcl_AppendResult(interp, "Error: cannot open file: ", zFile, NULL); + Tcl_AppendResult(interp, "Error: cannot open file: ", zFile, (char*)0); sqlite3_finalize(pStmt); return TCL_ERROR; } @@ -2162,7 +2379,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ for(i=0; i<nCol; i++){ /* check for null data, if so, bind as null */ if( (nNull>0 && strcmp(azCol[i], zNull)==0) - || strlen30(azCol[i])==0 + || strlen30(azCol[i])==0 ){ sqlite3_bind_null(pStmt, i+1); }else{ @@ -2241,35 +2458,37 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ ** The onecolumn method is the equivalent of: ** lindex [$db eval $sql] 0 */ - case DB_EXISTS: + case DB_EXISTS: case DB_ONECOLUMN: { + Tcl_Obj *pResult = 0; DbEvalContext sEval; if( objc!=3 ){ Tcl_WrongNumArgs(interp, 2, objv, "SQL"); return TCL_ERROR; } - dbEvalInit(&sEval, pDb, objv[2], 0); + dbEvalInit(&sEval, pDb, objv[2], 0, 0); rc = dbEvalStep(&sEval); if( choice==DB_ONECOLUMN ){ if( rc==TCL_OK ){ - Tcl_SetObjResult(interp, dbEvalColumnValue(&sEval, 0)); + pResult = dbEvalColumnValue(&sEval, 0); }else if( rc==TCL_BREAK ){ Tcl_ResetResult(interp); } }else if( rc==TCL_BREAK || rc==TCL_OK ){ - Tcl_SetObjResult(interp, Tcl_NewBooleanObj(rc==TCL_OK)); + pResult = Tcl_NewBooleanObj(rc==TCL_OK); } dbEvalFinalize(&sEval); + if( pResult ) Tcl_SetObjResult(interp, pResult); if( rc==TCL_BREAK ){ rc = TCL_OK; } break; } - + /* - ** $db eval $sql ?array? ?{ ...code... }? + ** $db eval ?options? $sql ?array? ?{ ...code... }? ** ** The SQL statement in $sql is evaluated. For each row, the values are ** placed in elements of the array named "array" and ...code... is executed. @@ -2278,8 +2497,22 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ ** that have the same name as the fields extracted by the query. */ case DB_EVAL: { + int evalFlags = 0; + const char *zOpt; + while( objc>3 && (zOpt = Tcl_GetString(objv[2]))!=0 && zOpt[0]=='-' ){ + if( strcmp(zOpt, "-withoutnulls")==0 ){ + evalFlags |= SQLITE_EVAL_WITHOUTNULLS; + } + else{ + Tcl_AppendResult(interp, "unknown option: \"", zOpt, "\"", (void*)0); + return TCL_ERROR; + } + objc--; + objv++; + } if( objc<3 || objc>5 ){ - Tcl_WrongNumArgs(interp, 2, objv, "SQL ?ARRAY-NAME? ?SCRIPT?"); + Tcl_WrongNumArgs(interp, 2, objv, + "?OPTIONS? SQL ?ARRAY-NAME? ?SCRIPT?"); return TCL_ERROR; } @@ -2287,7 +2520,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ DbEvalContext sEval; Tcl_Obj *pRet = Tcl_NewObj(); Tcl_IncrRefCount(pRet); - dbEvalInit(&sEval, pDb, objv[2], 0); + dbEvalInit(&sEval, pDb, objv[2], 0, 0); while( TCL_OK==(rc = dbEvalStep(&sEval)) ){ int i; int nCol; @@ -2303,56 +2536,71 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ } Tcl_DecrRefCount(pRet); }else{ - ClientData cd[2]; + ClientData cd2[2]; DbEvalContext *p; Tcl_Obj *pArray = 0; Tcl_Obj *pScript; - if( objc==5 && *(char *)Tcl_GetString(objv[3]) ){ + if( objc>=5 && *(char *)Tcl_GetString(objv[3]) ){ pArray = objv[3]; } pScript = objv[objc-1]; Tcl_IncrRefCount(pScript); - + p = (DbEvalContext *)Tcl_Alloc(sizeof(DbEvalContext)); - dbEvalInit(p, pDb, objv[2], pArray); + dbEvalInit(p, pDb, objv[2], pArray, evalFlags); - cd[0] = (void *)p; - cd[1] = (void *)pScript; - rc = DbEvalNextCmd(cd, interp, TCL_OK); + cd2[0] = (void *)p; + cd2[1] = (void *)pScript; + rc = DbEvalNextCmd(cd2, interp, TCL_OK); } break; } /* - ** $db function NAME [-argcount N] SCRIPT + ** $db function NAME [-argcount N] [-deterministic] SCRIPT ** ** Create a new SQL function called NAME. Whenever that function is ** called, invoke SCRIPT to evaluate the function. */ case DB_FUNCTION: { + int flags = SQLITE_UTF8; SqlFunc *pFunc; Tcl_Obj *pScript; char *zName; int nArg = -1; - if( objc==6 ){ - const char *z = Tcl_GetString(objv[3]); + int i; + if( objc<4 ){ + Tcl_WrongNumArgs(interp, 2, objv, "NAME ?SWITCHES? SCRIPT"); + return TCL_ERROR; + } + for(i=3; i<(objc-1); i++){ + const char *z = Tcl_GetString(objv[i]); int n = strlen30(z); if( n>2 && strncmp(z, "-argcount",n)==0 ){ - if( Tcl_GetIntFromObj(interp, objv[4], &nArg) ) return TCL_ERROR; + if( i==(objc-2) ){ + Tcl_AppendResult(interp, "option requires an argument: ", z,(char*)0); + return TCL_ERROR; + } + if( Tcl_GetIntFromObj(interp, objv[i+1], &nArg) ) return TCL_ERROR; if( nArg<0 ){ Tcl_AppendResult(interp, "number of arguments must be non-negative", (char*)0); return TCL_ERROR; } + i++; + }else + if( n>2 && strncmp(z, "-deterministic",n)==0 ){ + flags |= SQLITE_DETERMINISTIC; + }else{ + Tcl_AppendResult(interp, "bad option \"", z, + "\": must be -argcount or -deterministic", (char*)0 + ); + return TCL_ERROR; } - pScript = objv[5]; - }else if( objc!=4 ){ - Tcl_WrongNumArgs(interp, 2, objv, "NAME [-argcount N] SCRIPT"); - return TCL_ERROR; - }else{ - pScript = objv[3]; } + + pScript = objv[objc-1]; zName = Tcl_GetStringFromObj(objv[2], 0); pFunc = findSqlFunc(pDb, zName); if( pFunc==0 ) return TCL_ERROR; @@ -2362,7 +2610,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ pFunc->pScript = pScript; Tcl_IncrRefCount(pScript); pFunc->useEvalObjv = safeToUseEvalObjv(interp, pScript); - rc = sqlite3_create_function(pDb->db, zName, nArg, SQLITE_UTF8, + rc = sqlite3_create_function(pDb->db, zName, nArg, flags, pFunc, tclSqlFunc, 0, 0); if( rc!=SQLITE_OK ){ rc = TCL_ERROR; @@ -2454,7 +2702,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ } /* - ** $db last_insert_rowid + ** $db last_insert_rowid ** ** Return an integer which is the ROWID for the most recent insert. */ @@ -2476,7 +2724,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ */ /* $db progress ?N CALLBACK? - ** + ** ** Invoke the given callback every N virtual machine opcodes while executing ** queries. */ @@ -2544,7 +2792,8 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ }else{ pDb->zProfile = 0; } -#if !defined(SQLITE_OMIT_TRACE) && !defined(SQLITE_OMIT_FLOATING_POINT) +#if !defined(SQLITE_OMIT_TRACE) && !defined(SQLITE_OMIT_FLOATING_POINT) && \ + !defined(SQLITE_OMIT_DEPRECATED) if( pDb->zProfile ){ pDb->interp = interp; sqlite3_profile(pDb->db, DbProfileHandler, pDb); @@ -2562,7 +2811,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ ** Change the encryption key on the currently open database. */ case DB_REKEY: { -#ifdef SQLITE_HAS_CODEC +#if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_CODEC_FROM_TCL) int nKey; void *pKey; #endif @@ -2570,7 +2819,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ Tcl_WrongNumArgs(interp, 2, objv, "KEY"); return TCL_ERROR; } -#ifdef SQLITE_HAS_CODEC +#if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_CODEC_FROM_TCL) pKey = Tcl_GetByteArrayFromObj(objv[2], &nKey); rc = sqlite3_rekey(pDb->db, pKey, nKey); if( rc ){ @@ -2583,7 +2832,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ /* $db restore ?DATABASE? FILENAME ** - ** Open a database file named FILENAME. Transfer the content + ** Open a database file named FILENAME. Transfer the content ** of FILENAME into the local database DATABASE (default: "main"). */ case DB_RESTORE: { @@ -2603,7 +2852,8 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ Tcl_WrongNumArgs(interp, 2, objv, "?DATABASE? FILENAME"); return TCL_ERROR; } - rc = sqlite3_open_v2(zSrcFile, &pSrc, SQLITE_OPEN_READONLY, 0); + rc = sqlite3_open_v2(zSrcFile, &pSrc, + SQLITE_OPEN_READONLY | pDb->openFlags, 0); if( rc!=SQLITE_OK ){ Tcl_AppendResult(interp, "cannot open source database: ", sqlite3_errmsg(pSrc), (char*)0); @@ -2641,9 +2891,9 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ } /* - ** $db status (step|sort|autoindex) + ** $db status (step|sort|autoindex|vmstep) ** - ** Display SQLITE_STMTSTATUS_FULLSCAN_STEP or + ** Display SQLITE_STMTSTATUS_FULLSCAN_STEP or ** SQLITE_STMTSTATUS_SORT for the most recent eval. */ case DB_STATUS: { @@ -2660,16 +2910,18 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ v = pDb->nSort; }else if( strcmp(zOp, "autoindex")==0 ){ v = pDb->nIndex; + }else if( strcmp(zOp, "vmstep")==0 ){ + v = pDb->nVMStep; }else{ - Tcl_AppendResult(interp, - "bad argument: should be autoindex, step, or sort", + Tcl_AppendResult(interp, + "bad argument: should be autoindex, step, sort or vmstep", (char*)0); return TCL_ERROR; } Tcl_SetObjResult(interp, Tcl_NewIntObj(v)); break; } - + /* ** $db timeout MILLESECONDS ** @@ -2685,11 +2937,11 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ sqlite3_busy_timeout(pDb->db, ms); break; } - + /* ** $db total_changes ** - ** Return the number of rows that were modified, inserted, or deleted + ** Return the number of rows that were modified, inserted, or deleted ** since the database handle was created. */ case DB_TOTAL_CHANGES: { @@ -2730,7 +2982,8 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ }else{ pDb->zTrace = 0; } -#if !defined(SQLITE_OMIT_TRACE) && !defined(SQLITE_OMIT_FLOATING_POINT) +#if !defined(SQLITE_OMIT_TRACE) && !defined(SQLITE_OMIT_FLOATING_POINT) && \ + !defined(SQLITE_OMIT_DEPRECATED) if( pDb->zTrace ){ pDb->interp = interp; sqlite3_trace(pDb->db, DbTraceHandler, pDb); @@ -2742,6 +2995,88 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ break; } + /* $db trace_v2 ?CALLBACK? ?MASK? + ** + ** Make arrangements to invoke the CALLBACK routine for each trace event + ** matching the mask that is generated. The parameters are appended to + ** CALLBACK before it is executed. + */ + case DB_TRACE_V2: { + if( objc>4 ){ + Tcl_WrongNumArgs(interp, 2, objv, "?CALLBACK? ?MASK?"); + return TCL_ERROR; + }else if( objc==2 ){ + if( pDb->zTraceV2 ){ + Tcl_AppendResult(interp, pDb->zTraceV2, (char*)0); + } + }else{ + char *zTraceV2; + int len; + Tcl_WideInt wMask = 0; + if( objc==4 ){ + static const char *TTYPE_strs[] = { + "statement", "profile", "row", "close", 0 + }; + enum TTYPE_enum { + TTYPE_STMT, TTYPE_PROFILE, TTYPE_ROW, TTYPE_CLOSE + }; + int i; + if( TCL_OK!=Tcl_ListObjLength(interp, objv[3], &len) ){ + return TCL_ERROR; + } + for(i=0; i<len; i++){ + Tcl_Obj *pObj; + int ttype; + if( TCL_OK!=Tcl_ListObjIndex(interp, objv[3], i, &pObj) ){ + return TCL_ERROR; + } + if( Tcl_GetIndexFromObj(interp, pObj, TTYPE_strs, "trace type", + 0, &ttype)!=TCL_OK ){ + Tcl_WideInt wType; + Tcl_Obj *pError = Tcl_DuplicateObj(Tcl_GetObjResult(interp)); + Tcl_IncrRefCount(pError); + if( TCL_OK==Tcl_GetWideIntFromObj(interp, pObj, &wType) ){ + Tcl_DecrRefCount(pError); + wMask |= wType; + }else{ + Tcl_SetObjResult(interp, pError); + Tcl_DecrRefCount(pError); + return TCL_ERROR; + } + }else{ + switch( (enum TTYPE_enum)ttype ){ + case TTYPE_STMT: wMask |= SQLITE_TRACE_STMT; break; + case TTYPE_PROFILE: wMask |= SQLITE_TRACE_PROFILE; break; + case TTYPE_ROW: wMask |= SQLITE_TRACE_ROW; break; + case TTYPE_CLOSE: wMask |= SQLITE_TRACE_CLOSE; break; + } + } + } + }else{ + wMask = SQLITE_TRACE_STMT; /* use the "legacy" default */ + } + if( pDb->zTraceV2 ){ + Tcl_Free(pDb->zTraceV2); + } + zTraceV2 = Tcl_GetStringFromObj(objv[2], &len); + if( zTraceV2 && len>0 ){ + pDb->zTraceV2 = Tcl_Alloc( len + 1 ); + memcpy(pDb->zTraceV2, zTraceV2, len+1); + }else{ + pDb->zTraceV2 = 0; + } +#if !defined(SQLITE_OMIT_TRACE) && !defined(SQLITE_OMIT_FLOATING_POINT) + if( pDb->zTraceV2 ){ + pDb->interp = interp; + sqlite3_trace_v2(pDb->db, (unsigned)wMask, DbTraceV2Handler, pDb); + }else{ + sqlite3_trace_v2(pDb->db, 0, 0, 0); + } +#endif + } + break; + } + /* $db transaction [-deferred|-immediate|-exclusive] SCRIPT ** ** Start a new transaction (if we are not already in the midst of a @@ -2794,7 +3129,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ /* If using NRE, schedule a callback to invoke the script pScript, then ** a second callback to commit (or rollback) the transaction or savepoint ** opened above. If not using NRE, evaluate the script directly, then - ** call function DbTransPostCmd() to commit (or rollback) the transaction + ** call function DbTransPostCmd() to commit (or rollback) the transaction ** or savepoint. */ if( DbUseNre() ){ Tcl_NRAddCallback(interp, DbTransPostCmd, cd, 0, 0, 0); @@ -2825,14 +3160,14 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ Tcl_DecrRefCount(pDb->pUnlockNotify); pDb->pUnlockNotify = 0; } - + if( objc==3 ){ xNotify = DbUnlockNotify; pNotifyArg = (void *)pDb; pDb->pUnlockNotify = objv[2]; Tcl_IncrRefCount(pDb->pUnlockNotify); } - + if( sqlite3_unlock_notify(pDb->db, xNotify, pNotifyArg) ){ Tcl_AppendResult(interp, sqlite3_errmsg(pDb->db), (char*)0); rc = TCL_ERROR; @@ -2843,49 +3178,111 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ } /* + ** $db preupdate_hook count + ** $db preupdate_hook hook ?SCRIPT? + ** $db preupdate_hook new INDEX + ** $db preupdate_hook old INDEX + */ + case DB_PREUPDATE: { +#ifndef SQLITE_ENABLE_PREUPDATE_HOOK + Tcl_AppendResult(interp, "preupdate_hook was omitted at compile-time", + (char*)0); + rc = TCL_ERROR; +#else + static const char *azSub[] = {"count", "depth", "hook", "new", "old", 0}; + enum DbPreupdateSubCmd { + PRE_COUNT, PRE_DEPTH, PRE_HOOK, PRE_NEW, PRE_OLD + }; + int iSub; + + if( objc<3 ){ + Tcl_WrongNumArgs(interp, 2, objv, "SUB-COMMAND ?ARGS?"); + } + if( Tcl_GetIndexFromObj(interp, objv[2], azSub, "sub-command", 0, &iSub) ){ + return TCL_ERROR; + } + + switch( (enum DbPreupdateSubCmd)iSub ){ + case PRE_COUNT: { + int nCol = sqlite3_preupdate_count(pDb->db); + Tcl_SetObjResult(interp, Tcl_NewIntObj(nCol)); + break; + } + + case PRE_HOOK: { + if( objc>4 ){ + Tcl_WrongNumArgs(interp, 2, objv, "hook ?SCRIPT?"); + return TCL_ERROR; + } + DbHookCmd(interp, pDb, (objc==4 ? objv[3] : 0), &pDb->pPreUpdateHook); + break; + } + + case PRE_DEPTH: { + Tcl_Obj *pRet; + if( objc!=3 ){ + Tcl_WrongNumArgs(interp, 3, objv, ""); + return TCL_ERROR; + } + pRet = Tcl_NewIntObj(sqlite3_preupdate_depth(pDb->db)); + Tcl_SetObjResult(interp, pRet); + break; + } + + case PRE_NEW: + case PRE_OLD: { + int iIdx; + sqlite3_value *pValue; + if( objc!=4 ){ + Tcl_WrongNumArgs(interp, 3, objv, "INDEX"); + return TCL_ERROR; + } + if( Tcl_GetIntFromObj(interp, objv[3], &iIdx) ){ + return TCL_ERROR; + } + + if( iSub==PRE_OLD ){ + rc = sqlite3_preupdate_old(pDb->db, iIdx, &pValue); + }else{ + assert( iSub==PRE_NEW ); + rc = sqlite3_preupdate_new(pDb->db, iIdx, &pValue); + } + + if( rc==SQLITE_OK ){ + Tcl_Obj *pObj; + pObj = Tcl_NewStringObj((char*)sqlite3_value_text(pValue), -1); + Tcl_SetObjResult(interp, pObj); + }else{ + Tcl_AppendResult(interp, sqlite3_errmsg(pDb->db), (char*)0); + return TCL_ERROR; + } + } + } +#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ + break; + } + + /* ** $db wal_hook ?script? ** $db update_hook ?script? ** $db rollback_hook ?script? */ - case DB_WAL_HOOK: - case DB_UPDATE_HOOK: + case DB_WAL_HOOK: + case DB_UPDATE_HOOK: case DB_ROLLBACK_HOOK: { - - /* set ppHook to point at pUpdateHook or pRollbackHook, depending on + /* set ppHook to point at pUpdateHook or pRollbackHook, depending on ** whether [$db update_hook] or [$db rollback_hook] was invoked. */ - Tcl_Obj **ppHook; - if( choice==DB_UPDATE_HOOK ){ - ppHook = &pDb->pUpdateHook; - }else if( choice==DB_WAL_HOOK ){ - ppHook = &pDb->pWalHook; - }else{ - ppHook = &pDb->pRollbackHook; - } - - if( objc!=2 && objc!=3 ){ + Tcl_Obj **ppHook = 0; + if( choice==DB_WAL_HOOK ) ppHook = &pDb->pWalHook; + if( choice==DB_UPDATE_HOOK ) ppHook = &pDb->pUpdateHook; + if( choice==DB_ROLLBACK_HOOK ) ppHook = &pDb->pRollbackHook; + if( objc>3 ){ Tcl_WrongNumArgs(interp, 2, objv, "?SCRIPT?"); return TCL_ERROR; } - if( *ppHook ){ - Tcl_SetObjResult(interp, *ppHook); - if( objc==3 ){ - Tcl_DecrRefCount(*ppHook); - *ppHook = 0; - } - } - if( objc==3 ){ - assert( !(*ppHook) ); - if( Tcl_GetCharLength(objv[2])>0 ){ - *ppHook = objv[2]; - Tcl_IncrRefCount(*ppHook); - } - } - - sqlite3_update_hook(pDb->db, (pDb->pUpdateHook?DbUpdateHandler:0), pDb); - sqlite3_rollback_hook(pDb->db,(pDb->pRollbackHook?DbRollbackHandler:0),pDb); - sqlite3_wal_hook(pDb->db,(pDb->pWalHook?DbWalHandler:0),pDb); + DbHookCmd(interp, pDb, (objc==3 ? objv[2] : 0), ppHook); break; } @@ -2908,7 +3305,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ ** Adaptor that provides an objCmd interface to the NRE-enabled ** interface implementation. */ -static int DbObjCmdAdaptor( +static int SQLITE_TCLAPI DbObjCmdAdaptor( void *cd, Tcl_Interp *interp, int objc, @@ -2933,7 +3330,12 @@ static int DbObjCmdAdaptor( ** The second argument is the name of the database file. ** */ -static int DbMain(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ +static int SQLITE_TCLAPI DbMain( + void *cd, + Tcl_Interp *interp, + int objc, + Tcl_Obj *const*objv +){ SqliteDb *p; const char *zArg; char *zErrMsg; @@ -2942,7 +3344,7 @@ static int DbMain(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ const char *zVfs = 0; int flags; Tcl_DString translatedFilename; -#ifdef SQLITE_HAS_CODEC +#if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_CODEC_FROM_TCL) void *pKey = 0; int nKey = 0; #endif @@ -2966,8 +3368,12 @@ static int DbMain(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ Tcl_AppendResult(interp,sqlite3_libversion(), (char*)0); return TCL_OK; } + if( strcmp(zArg,"-sourceid")==0 ){ + Tcl_AppendResult(interp,sqlite3_sourceid(), (char*)0); + return TCL_OK; + } if( strcmp(zArg,"-has-codec")==0 ){ -#ifdef SQLITE_HAS_CODEC +#if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_CODEC_FROM_TCL) Tcl_AppendResult(interp,"1",(char*)0); #else Tcl_AppendResult(interp,"0",(char*)0); @@ -2978,7 +3384,7 @@ static int DbMain(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ for(i=3; i+1<objc; i+=2){ zArg = Tcl_GetString(objv[i]); if( strcmp(zArg,"-key")==0 ){ -#ifdef SQLITE_HAS_CODEC +#if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_CODEC_FROM_TCL) pKey = Tcl_GetByteArrayFromObj(objv[i+1], &nKey); #endif }else if( strcmp(zArg, "-vfs")==0 ){ @@ -3033,10 +3439,10 @@ static int DbMain(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ } } if( objc<3 || (objc&1)!=1 ){ - Tcl_WrongNumArgs(interp, 1, objv, + Tcl_WrongNumArgs(interp, 1, objv, "HANDLE FILENAME ?-vfs VFSNAME? ?-readonly BOOLEAN? ?-create BOOLEAN?" " ?-nomutex BOOLEAN? ?-fullmutex BOOLEAN? ?-uri BOOLEAN?" -#ifdef SQLITE_HAS_CODEC +#if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_CODEC_FROM_TCL) " ?-key CODECKEY?" #endif ); @@ -3044,10 +3450,6 @@ static int DbMain(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ } zErrMsg = 0; p = (SqliteDb*)Tcl_Alloc( sizeof(*p) ); - if( p==0 ){ - Tcl_SetResult(interp, (char *)"malloc failed", TCL_STATIC); - return TCL_ERROR; - } memset(p, 0, sizeof(*p)); zFile = Tcl_GetStringFromObj(objv[2], 0); zFile = Tcl_TranslateFileName(interp, zFile, &translatedFilename); @@ -3062,7 +3464,7 @@ static int DbMain(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ }else{ zErrMsg = sqlite3_mprintf("%s", sqlite3_errstr(rc)); } -#ifdef SQLITE_HAS_CODEC +#if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_CODEC_FROM_TCL) if( p->db ){ sqlite3_key(p->db, pKey, nKey); } @@ -3074,6 +3476,7 @@ static int DbMain(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ return TCL_ERROR; } p->maxStmt = NUM_PREPARED_STMTS; + p->openFlags = flags & SQLITE_OPEN_URI; p->interp = interp; zArg = Tcl_GetStringFromObj(objv[1], 0); if( DbUseNre() ){ @@ -3133,9 +3536,13 @@ EXTERN int Sqlite3_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; } EXTERN int Tclsqlite3_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; } /* Because it accesses the file-system and uses persistent state, SQLite -** is not considered appropriate for safe interpreters. Hence, we deliberately -** omit the _SafeInit() interfaces. +** is not considered appropriate for safe interpreters. Hence, we cause +** the _SafeInit() interfaces return TCL_ERROR. */ +EXTERN int Sqlite3_SafeInit(Tcl_Interp *interp){ return TCL_ERROR; } +EXTERN int Sqlite3_SafeUnload(Tcl_Interp *interp, int flags){return TCL_ERROR;} + + #ifndef SQLITE_3_SUFFIX_ONLY int Sqlite_Init(Tcl_Interp *interp){ return Sqlite3_Init(interp); } @@ -3320,7 +3727,7 @@ static void MD5Init(MD5Context *ctx){ * Update context to reflect the concatenation of another buffer full * of bytes. */ -static +static void MD5Update(MD5Context *ctx, const unsigned char *buf, unsigned int len){ uint32 t; @@ -3366,7 +3773,7 @@ void MD5Update(MD5Context *ctx, const unsigned char *buf, unsigned int len){ } /* - * Final wrapup - pad to 64-byte boundary with the bit pattern + * Final wrapup - pad to 64-byte boundary with the bit pattern * 1 0* (64-bit count of bits processed, MSB-first) */ static void MD5Final(unsigned char digest[16], MD5Context *ctx){ @@ -3434,7 +3841,7 @@ static void MD5DigestToBase10x8(unsigned char digest[16], char zDigest[50]){ for(i=j=0; i<16; i+=2){ x = digest[i]*256 + digest[i+1]; if( i>0 ) zDigest[j++] = '-'; - sprintf(&zDigest[j], "%05u", x); + sqlite3_snprintf(50-j, &zDigest[j], "%05u", x); j += 5; } zDigest[j] = 0; @@ -3442,16 +3849,21 @@ static void MD5DigestToBase10x8(unsigned char digest[16], char zDigest[50]){ /* ** A TCL command for md5. The argument is the text to be hashed. The -** Result is the hash in base64. +** Result is the hash in base64. */ -static int md5_cmd(void*cd, Tcl_Interp *interp, int argc, const char **argv){ +static int SQLITE_TCLAPI md5_cmd( + void*cd, + Tcl_Interp *interp, + int argc, + const char **argv +){ MD5Context ctx; unsigned char digest[16]; char zBuf[50]; void (*converter)(unsigned char*, char*); if( argc!=2 ){ - Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0], + Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0], " TEXT\"", (char*)0); return TCL_ERROR; } @@ -3468,7 +3880,12 @@ static int md5_cmd(void*cd, Tcl_Interp *interp, int argc, const char **argv){ ** A TCL command to take the md5 hash of a file. The argument is the ** name of the file. */ -static int md5file_cmd(void*cd, Tcl_Interp*interp, int argc, const char **argv){ +static int SQLITE_TCLAPI md5file_cmd( + void*cd, + Tcl_Interp *interp, + int argc, + const char **argv +){ FILE *in; MD5Context ctx; void (*converter)(unsigned char*, char*); @@ -3476,13 +3893,13 @@ static int md5file_cmd(void*cd, Tcl_Interp*interp, int argc, const char **argv){ char zBuf[10240]; if( argc!=2 ){ - Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0], + Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0], " FILENAME\"", (char*)0); return TCL_ERROR; } in = fopen(argv[1],"rb"); if( in==0 ){ - Tcl_AppendResult(interp,"unable to open file \"", argv[1], + Tcl_AppendResult(interp,"unable to open file \"", argv[1], "\" for reading", (char*)0); return TCL_ERROR; } @@ -3548,8 +3965,12 @@ static void md5finalize(sqlite3_context *context){ MD5DigestToBase16(digest, zBuf); sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT); } -int Md5_Register(sqlite3 *db){ - int rc = sqlite3_create_function(db, "md5sum", -1, SQLITE_UTF8, 0, 0, +int Md5_Register( + sqlite3 *db, + char **pzErrMsg, + const sqlite3_api_routines *pThunk +){ + int rc = sqlite3_create_function(db, "md5sum", -1, SQLITE_UTF8, 0, 0, md5step, md5finalize); sqlite3_overload_function(db, "md5sum", -1); /* To exercise this API */ return rc; @@ -3596,7 +4017,7 @@ static const char *tclsh_main_loop(void); #ifdef SQLITE_TEST static void init_all(Tcl_Interp *); -static int init_all_cmd( +static int SQLITE_TCLAPI init_all_cmd( ClientData cd, Tcl_Interp *interp, int objc, @@ -3626,7 +4047,7 @@ static int init_all_cmd( ** to use the sqlite3_prepare_v2() function to prepare statements. If it ** is false, sqlite3_prepare(). */ -static int db_use_legacy_prepare_cmd( +static int SQLITE_TCLAPI db_use_legacy_prepare_cmd( ClientData cd, Tcl_Interp *interp, int objc, @@ -3663,7 +4084,7 @@ static int db_use_legacy_prepare_cmd( ** return the text representation of the most recently used statement ** handle. */ -static int db_last_stmt_ptr( +static int SQLITE_TCLAPI db_last_stmt_ptr( ClientData cd, Tcl_Interp *interp, int objc, @@ -3694,13 +4115,13 @@ static int db_last_stmt_ptr( return TCL_OK; } -#endif +#endif /* SQLITE_TEST */ /* ** Configure the interpreter passed as the first argument to have access ** to the commands and linked variables that make up: ** -** * the [sqlite3] extension itself, +** * the [sqlite3] extension itself, ** ** * If SQLITE_TCLMD5 or SQLITE_TEST is defined, the Md5 commands, and ** @@ -3714,17 +4135,6 @@ static void init_all(Tcl_Interp *interp){ Md5_Init(interp); #endif - /* Install the [register_dbstat_vtab] command to access the implementation - ** of virtual table dbstat (source file test_stat.c). This command is - ** required for testfixture and sqlite3_analyzer, but not by the production - ** Tcl extension. */ -#if defined(SQLITE_TEST) || TCLSH==2 - { - extern int SqlitetestStat_Init(Tcl_Interp*); - SqlitetestStat_Init(interp); - } -#endif - #ifdef SQLITE_TEST { extern int Sqliteconfig_Init(Tcl_Interp*); @@ -3761,7 +4171,12 @@ static void init_all(Tcl_Interp *interp){ extern int Sqlitemultiplex_Init(Tcl_Interp*); extern int SqliteSuperlock_Init(Tcl_Interp*); extern int SqlitetestSyscall_Init(Tcl_Interp*); - +#if defined(SQLITE_ENABLE_SESSION) && defined(SQLITE_ENABLE_PREUPDATE_HOOK) + extern int TestSession_Init(Tcl_Interp*); +#endif + extern int Fts5tcl_Init(Tcl_Interp *); + extern int SqliteRbu_Init(Tcl_Interp*); + extern int Sqlitetesttcl_Init(Tcl_Interp*); #if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4) extern int Sqlitetestfts3_Init(Tcl_Interp *interp); #endif @@ -3794,7 +4209,7 @@ static void init_all(Tcl_Interp *interp){ Sqlitetesttclvar_Init(interp); Sqlitetestfs_Init(interp); SqlitetestThread_Init(interp); - SqlitetestOnefile_Init(interp); + SqlitetestOnefile_Init(); SqlitetestOsinst_Init(interp); Sqlitetestbackup_Init(interp); Sqlitetestintarray_Init(interp); @@ -3804,6 +4219,12 @@ static void init_all(Tcl_Interp *interp){ Sqlitemultiplex_Init(interp); SqliteSuperlock_Init(interp); SqlitetestSyscall_Init(interp); +#if defined(SQLITE_ENABLE_SESSION) && defined(SQLITE_ENABLE_PREUPDATE_HOOK) + TestSession_Init(interp); +#endif + Fts5tcl_Init(interp); + SqliteRbu_Init(interp); + Sqlitetesttcl_Init(interp); #if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4) Sqlitetestfts3_Init(interp); @@ -3832,7 +4253,7 @@ static void init_all(Tcl_Interp *interp){ #endif #define TCLSH_MAIN main /* Needed to fake out mktclapp */ -int TCLSH_MAIN(int argc, char **argv){ +int SQLITE_CDECL TCLSH_MAIN(int argc, char **argv){ Tcl_Interp *interp; #if !defined(_WIN32_WCE) |