]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Initial work on the Tcl API interface to the new sqlite3_trace_v2() function.
authormistachkin <mistachkin@noemail.net>
Thu, 14 Jul 2016 21:26:09 +0000 (21:26 +0000)
committermistachkin <mistachkin@noemail.net>
Thu, 14 Jul 2016 21:26:09 +0000 (21:26 +0000)
FossilOrigin-Name: 7b59fa40a01c89cc98414d90a798169c26e04256

manifest
manifest.uuid
src/tclsqlite.c

index 0d6dec24f5f39333cafb5666af8e5c99d86cab77..b880ee391db6e1172996f8929719157122a2b6e0 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Fix\scopy/paste\stypo\sin\sthe\snew\ssqlite3_expanded_sql()\sfunction.
-D 2016-07-14T09:22:16.664
+C Initial\swork\son\sthe\sTcl\sAPI\sinterface\sto\sthe\snew\ssqlite3_trace_v2()\sfunction.
+D 2016-07-14T21:26:09.090
 F Makefile.in 6c20d44f72d4564f11652b26291a214c8367e5db
 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
 F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a
@@ -392,7 +392,7 @@ F src/sqliteInt.h 48cd97eb134665348393dfe277b4c14d1085bfc7
 F src/sqliteLimit.h c0373387c287c8d0932510b5547ecde31b5da247
 F src/status.c 5b18f9526900f61189ab0b83f1ef41d9f871a2ab
 F src/table.c 5226df15ab9179b9ed558d89575ea0ce37b03fc9
-F src/tclsqlite.c 25fbbbb97f76dbfd113153fb63f52d7ecfac5dd0
+F src/tclsqlite.c 361167055f0d0d9883b729f1db698edcd3b46065
 F src/test1.c 5124aff86fba753a6994e9621696ccfdc8bbf24e
 F src/test2.c 5586f43fcd9a1be0830793cf9d354082c261b25b
 F src/test3.c c75c8af0eadb335236c9e61b51044c58a8f7dd59
@@ -1505,7 +1505,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 989de2d5b5e7155654d3eebadb9651b23f422c58
-R 22fe35d8085a083776873f2a488c7041
+P e7d18c70d2b8f09c9f5b978fe3d69d1088e42322
+R 85912bc13f3699d6087855b14808f819
 U mistachkin
-Z 0cf3ee8d7f2e128326cc9e0cdcd59d22
+Z 312d6409632132f635d6a5f40333e628
index 9a4a235af8f57ec68d264e7ff3b89fea34a3333f..f5bb4e0461baea256e358a9d2a3850fd7fa0135c 100644 (file)
@@ -1 +1 @@
-e7d18c70d2b8f09c9f5b978fe3d69d1088e42322
\ No newline at end of file
+7b59fa40a01c89cc98414d90a798169c26e04256
\ No newline at end of file
index 476bdf235e98c10d0c5d567c4e6303840a1b725f..bcae05d79599226af7b5fa9610cdcaa82f330dbe 100644 (file)
@@ -133,6 +133,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 */
@@ -192,7 +193,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.
     */
@@ -233,8 +234,8 @@ static int incrblobClose(ClientData instanceData, Tcl_Interp *interp){
 ** Read data from an incremental blob channel.
 */
 static int incrblobInput(
-  ClientData instanceData, 
-  char *buf, 
+  ClientData instanceData,
+  char *buf,
   int bufSize,
   int *errorCodePtr
 ){
@@ -265,8 +266,8 @@ static int incrblobInput(
 ** Write data to an incremental blob channel.
 */
 static int incrblobOutput(
-  ClientData instanceData, 
-  CONST char *buf, 
+  ClientData instanceData,
+  CONST char *buf,
   int toWrite,
   int *errorCodePtr
 ){
@@ -298,7 +299,7 @@ static int incrblobOutput(
 ** Seek an incremental blob channel.
 */
 static int incrblobSeek(
-  ClientData instanceData, 
+  ClientData instanceData,
   long offset,
   int seekMode,
   int *errorCodePtr
@@ -323,8 +324,8 @@ static int incrblobSeek(
 }
 
 
-static void incrblobWatch(ClientData instanceData, int mode){ 
-  /* NO-OP */ 
+static void incrblobWatch(ClientData instanceData, int mode){
+  /* NO-OP */
 }
 static int incrblobHandle(ClientData instanceData, int dir, ClientData *hPtr){
   return TCL_ERROR;
@@ -352,11 +353,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
 ){
@@ -438,7 +439,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;
@@ -508,6 +509,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);
   }
@@ -587,6 +591,82 @@ static void DbTraceHandler(void *cd, const char *zSql){
 }
 #endif
 
+#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
+
 #ifndef SQLITE_OMIT_TRACE
 /*
 ** This routine is called by the SQLite profile handler after a statement
@@ -637,9 +717,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 +733,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);
@@ -695,11 +775,11 @@ static void DbUnlockNotify(void **apArg, int nArg){
 ** Pre-update hook callback.
 */
 static void DbPreUpdateHandler(
-  void *p, 
+  void *p,
   sqlite3 *db,
   int op,
-  const char *zDb, 
-  const char *zTbl, 
+  const char *zDb,
+  const char *zTbl,
   sqlite_int64 iKey1,
   sqlite_int64 iKey2
 ){
@@ -727,10 +807,10 @@ static void DbPreUpdateHandler(
 #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;
@@ -815,7 +895,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.
@@ -823,15 +903,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: {
@@ -866,7 +946,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;
       }
     }
@@ -881,7 +961,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;
@@ -983,7 +1063,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";
@@ -1075,12 +1155,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.
       */
@@ -1099,7 +1179,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(
@@ -1155,7 +1235,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]==';')
     ){
@@ -1181,7 +1261,7 @@ static int dbPrepareAndBind(
       break;
     }
   }
-  
+
   /* If no prepared statement was found. Compile the SQL text. Also allocate
   ** a new SqlPreparedStmt structure.  */
   if( pPreStmt==0 ){
@@ -1227,7 +1307,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]=='@') ){
@@ -1315,8 +1395,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;
@@ -1370,8 +1450,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}
@@ -1492,7 +1572,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. */
@@ -1580,11 +1660,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() ) { ... }
@@ -1630,11 +1710,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
@@ -1659,7 +1739,7 @@ static int DbEvalNextCmd(
 }
 
 /*
-** This function is used by the implementations of the following database 
+** This function is used by the implementations of the following database
 ** handle sub-commands:
 **
 **   $db update_hook ?SCRIPT?
@@ -1726,9 +1806,10 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
     "preupdate",          "profile",           "progress",
     "rekey",              "restore",           "rollback_hook",
     "status",             "timeout",           "total_changes",
-    "trace",              "transaction",       "unlock_notify",
-    "update_hook",        "version",           "wal_hook",
-    0                    
+    "trace",              "trace_v2",          "transaction",
+    "unlock_notify",      "update_hook",       "version",
+    "wal_hook",
+    0
   };
   enum DB_enum {
     DB_AUTHORIZER,        DB_BACKUP,           DB_BUSY,
@@ -1741,8 +1822,9 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
     DB_PREUPDATE,         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_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 */
 
@@ -1928,7 +2010,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{
@@ -1942,7 +2024,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;
@@ -1953,7 +2035,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: {
@@ -2000,7 +2082,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;
@@ -2126,7 +2208,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;
     }
@@ -2155,7 +2237,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;
@@ -2244,7 +2326,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{
@@ -2323,7 +2405,7 @@ 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;
@@ -2351,7 +2433,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
     }
     break;
   }
-   
+
   /*
   **    $db eval $sql ?array? ?{  ...code... }?
   **
@@ -2397,7 +2479,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);
 
@@ -2444,7 +2526,7 @@ 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, 
+        Tcl_AppendResult(interp, "bad option \"", z,
             "\": must be -argcount or -deterministic", 0
         );
         return TCL_ERROR;
@@ -2553,7 +2635,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.
   */
@@ -2575,7 +2657,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.
   */
@@ -2682,7 +2764,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: {
@@ -2743,7 +2825,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: {
@@ -2761,15 +2843,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
   **
@@ -2785,11 +2867,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: {
@@ -2842,6 +2924,53 @@ 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{
+      Tcl_WideInt wMask;
+      char *zTraceV2;
+      int len;
+      if( objc==4 ){
+        if( TCL_OK!=Tcl_GetWideIntFromObj(interp, objv[3], &wMask) ){
+          return TCL_ERROR;
+        }
+      }else{
+        wMask = SQLITE_TRACE_STMT;
+      }
+      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
@@ -2894,7 +3023,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);
@@ -2925,14 +3054,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;
@@ -3031,13 +3160,13 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
   **    $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 = 0; 
+    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;
@@ -3198,7 +3327,7 @@ 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?"
 #if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_CODEC_FROM_TCL)
@@ -3490,7 +3619,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;
 
@@ -3536,7 +3665,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){
@@ -3612,7 +3741,7 @@ 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){
   MD5Context ctx;
@@ -3621,7 +3750,7 @@ static int md5_cmd(void*cd, Tcl_Interp *interp, int argc, const char **argv){
   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;
   }
@@ -3646,13 +3775,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;
   }
@@ -3719,7 +3848,7 @@ static void md5finalize(sqlite3_context *context){
   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 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;
@@ -3870,7 +3999,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
 **