Index: third_party/sqlite/src/src/tclsqlite.c |
diff --git a/third_party/sqlite/src/src/tclsqlite.c b/third_party/sqlite/src/src/tclsqlite.c |
index f024317e9098399b31d96e723cab0719959a1cf1..5b52bf0c91ca14652974173a6e6d66d22bb2ee07 100644 |
--- a/third_party/sqlite/src/src/tclsqlite.c |
+++ b/third_party/sqlite/src/src/tclsqlite.c |
@@ -30,10 +30,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> |
/* |
@@ -133,6 +140,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 */ |
@@ -140,6 +148,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) */ |
@@ -153,6 +162,7 @@ struct SqliteDb { |
IncrblobChannel *pIncrblob;/* Linked list of open incrblob channels */ |
int nStep, nSort, nIndex; /* Statistics 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 |
@@ -190,7 +200,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. |
*/ |
@@ -201,7 +211,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; |
@@ -230,9 +243,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 |
){ |
@@ -262,9 +275,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 |
){ |
@@ -295,8 +308,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 |
@@ -321,10 +334,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; |
} |
@@ -350,11 +370,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 |
){ |
@@ -436,7 +456,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; |
@@ -483,7 +503,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); |
@@ -506,6 +526,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); |
} |
@@ -518,6 +541,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); |
} |
@@ -564,7 +590,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. |
@@ -584,6 +611,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. |
*/ |
@@ -632,9 +736,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; |
@@ -648,7 +752,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); |
@@ -685,23 +789,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)); |
@@ -770,7 +914,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. |
@@ -778,15 +922,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: { |
@@ -821,7 +965,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; |
} |
} |
@@ -836,7 +980,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; |
@@ -938,7 +1082,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"; |
@@ -1009,7 +1153,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 */ |
@@ -1030,12 +1174,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. |
*/ |
@@ -1054,7 +1198,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( |
@@ -1110,7 +1254,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]==';') |
){ |
@@ -1136,7 +1280,7 @@ static int dbPrepareAndBind( |
break; |
} |
} |
- |
+ |
/* If no prepared statement was found. Compile the SQL text. Also allocate |
** a new SqlPreparedStmt structure. */ |
if( pPreStmt==0 ){ |
@@ -1182,7 +1326,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]=='@') ){ |
@@ -1270,8 +1414,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; |
@@ -1325,8 +1469,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} |
@@ -1447,7 +1591,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. */ |
@@ -1535,11 +1679,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() ) { ... } |
@@ -1556,7 +1700,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 */ |
@@ -1585,11 +1729,11 @@ static int DbEvalNextCmd( |
} |
} |
- /* 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 |
@@ -1614,6 +1758,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 |
@@ -1626,7 +1810,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; |
@@ -1638,11 +1827,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, |
@@ -1652,11 +1843,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 */ |
@@ -1750,7 +1942,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); |
@@ -1841,7 +2034,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{ |
@@ -1855,7 +2048,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; |
@@ -1866,7 +2059,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: { |
@@ -1913,7 +2106,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; |
@@ -2039,7 +2232,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; |
} |
@@ -2068,7 +2261,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; |
@@ -2114,7 +2307,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; |
} |
@@ -2157,7 +2350,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{ |
@@ -2236,8 +2429,9 @@ 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"); |
@@ -2248,21 +2442,22 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ |
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... }? |
** |
@@ -2308,7 +2503,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ |
} |
pScript = objv[objc-1]; |
Tcl_IncrRefCount(pScript); |
- |
+ |
p = (DbEvalContext *)Tcl_Alloc(sizeof(DbEvalContext)); |
dbEvalInit(p, pDb, objv[2], pArray); |
@@ -2341,7 +2536,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ |
int n = strlen30(z); |
if( n>2 && strncmp(z, "-argcount",n)==0 ){ |
if( i==(objc-2) ){ |
- Tcl_AppendResult(interp, "option requires an argument: ", z, 0); |
+ Tcl_AppendResult(interp, "option requires an argument: ", z,(char*)0); |
return TCL_ERROR; |
} |
if( Tcl_GetIntFromObj(interp, objv[i+1], &nArg) ) return TCL_ERROR; |
@@ -2355,8 +2550,8 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ |
if( n>2 && strncmp(z, "-deterministic",n)==0 ){ |
flags |= SQLITE_DETERMINISTIC; |
}else{ |
- Tcl_AppendResult(interp, "bad option \"", z, |
- "\": must be -argcount or -deterministic", 0 |
+ Tcl_AppendResult(interp, "bad option \"", z, |
+ "\": must be -argcount or -deterministic", (char*)0 |
); |
return TCL_ERROR; |
} |
@@ -2464,7 +2659,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. |
*/ |
@@ -2486,7 +2681,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. |
*/ |
@@ -2554,7 +2749,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); |
@@ -2572,7 +2768,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 |
@@ -2580,7 +2776,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 ){ |
@@ -2593,7 +2789,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: { |
@@ -2613,7 +2809,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); |
@@ -2653,7 +2850,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ |
/* |
** $db status (step|sort|autoindex) |
** |
- ** Display SQLITE_STMTSTATUS_FULLSCAN_STEP or |
+ ** Display SQLITE_STMTSTATUS_FULLSCAN_STEP or |
** SQLITE_STMTSTATUS_SORT for the most recent eval. |
*/ |
case DB_STATUS: { |
@@ -2671,15 +2868,15 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ |
}else if( strcmp(zOp, "autoindex")==0 ){ |
v = pDb->nIndex; |
}else{ |
- Tcl_AppendResult(interp, |
- "bad argument: should be autoindex, step, or sort", |
+ Tcl_AppendResult(interp, |
+ "bad argument: should be autoindex, step, or sort", |
(char*)0); |
return TCL_ERROR; |
} |
Tcl_SetObjResult(interp, Tcl_NewIntObj(v)); |
break; |
} |
- |
+ |
/* |
** $db timeout MILLESECONDS |
** |
@@ -2695,11 +2892,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: { |
@@ -2740,7 +2937,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); |
@@ -2752,6 +2950,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 |
@@ -2804,7 +3084,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); |
@@ -2835,14 +3115,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; |
@@ -2853,49 +3133,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; |
} |
@@ -2918,7 +3260,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, |
@@ -2943,7 +3285,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; |
@@ -2952,7 +3299,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 |
@@ -2976,8 +3323,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); |
@@ -2988,7 +3339,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 ){ |
@@ -3043,10 +3394,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 |
); |
@@ -3072,7 +3423,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); |
} |
@@ -3084,6 +3435,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() ){ |
@@ -3143,9 +3495,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); } |
@@ -3330,7 +3686,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; |
@@ -3376,7 +3732,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){ |
@@ -3452,16 +3808,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; |
} |
@@ -3478,7 +3839,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*); |
@@ -3486,13 +3852,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; |
} |
@@ -3558,8 +3924,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; |
@@ -3606,7 +3976,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, |
@@ -3636,7 +4006,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, |
@@ -3673,7 +4043,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, |
@@ -3710,7 +4080,7 @@ static int db_last_stmt_ptr( |
** 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 |
** |
@@ -3760,8 +4130,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 +4168,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,8 +4178,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); |
@@ -3834,7 +4212,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) |