]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Increase resolution of time-of-day on unix. Add an experimental
authordrh <drh@noemail.net>
Mon, 29 Aug 2005 23:00:03 +0000 (23:00 +0000)
committerdrh <drh@noemail.net>
Mon, 29 Aug 2005 23:00:03 +0000 (23:00 +0000)
sqlite3_profile() API. (CVS 2639)

FossilOrigin-Name: ed2ca0873fa89d6cfd123541d5d1c6b92c72b6ab

13 files changed:
manifest
manifest.uuid
src/build.c
src/main.c
src/os_unix.c
src/sqlite.h.in
src/sqliteInt.h
src/tclsqlite.c
src/test1.c
src/vdbeInt.h
src/vdbeapi.c
test/tclsqlite.test
test/trace.test

index 799cedf05c833dec054e331d120d83a76518ca80..5ecf55d30d15d07b733512b9d6b8cfe1877bad40 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Initialize\sa\slocal\svariable\sto\savoid\sa\snuisance\scompiler\swarning.\nTicket\s#1394.\s(CVS\s2638)
-D 2005-08-29T16:40:53
+C Increase\sresolution\sof\stime-of-day\son\sunix.\s\sAdd\san\sexperimental\nsqlite3_profile()\sAPI.\s(CVS\s2639)
+D 2005-08-29T23:00:04
 F Makefile.in 12784cdce5ffc8dfb707300c34e4f1eb3b8a14f1
 F Makefile.linux-gcc 06be33b2a9ad4f005a5f42b22c4a19dab3cbb5c7
 F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028
@@ -34,7 +34,7 @@ F src/attach.c 4b21689700a72ae281fa85dbaff06b2a62bd49ee
 F src/auth.c 31e2304bef67f44d635655f44234387ea7d21454
 F src/btree.c 5b3bc015c49a41c025cfdf8ad36051f3007e2cb0
 F src/btree.h 1ed561263ca0e335bc3e81d761c9d5ff8c22f61e
-F src/build.c 55e7e915a538bb25b1b7448cfb9189cccba554b2
+F src/build.c 150902037cf8f7a9a09b31b753181a1e5aa23633
 F src/callback.c 9a1162c8f9dae9fad6d548339669aacb5f6cf76b
 F src/complete.c 4de937dfdd4c79a501772ab2035b26082f337a79
 F src/date.c 7444b0900a28da77e57e3337a636873cff0ae940
@@ -46,13 +46,13 @@ F src/hash.c 2b1b13f7400e179631c83a1be0c664608c8f021f
 F src/hash.h 1b0c445e1c89ff2aaad9b4605ba61375af001e84
 F src/insert.c 484c73bc1309f283a31baa0e114f3ee980536397
 F src/legacy.c d58ea507bce885298a2c8c3cbb0f4bff5d47830b
-F src/main.c 60eb224fa5fe65e92dcdfdc542c94bae5e4e2e84
+F src/main.c 8bcd1d2ed92dcb24bafb770eae6e4afce8ddcbde
 F src/md5.c 7ae1c39044b95de2f62e066f47bb1deb880a1070
 F src/os.h c4b34bd4d6fea51a420f337468b907f4edecb161
 F src/os_common.h 0e7f428ba0a6c40a61bc56c4e96f493231301b73
 F src/os_test.c 91e5f22dd89491e5e1554820e715805f43fa4ece
 F src/os_test.h 903c93554c23d88f34f667f1979e4a1cee792af3
-F src/os_unix.c 7fae44e25c6137340b786b83ecb29db6ba525a64
+F src/os_unix.c b4c4592589113db088662ef7570967ec36022b5d
 F src/os_unix.h 5768d56d28240d3fe4537fac08cc85e4fb52279e
 F src/os_win.c fe7b99cfcfb61d9bf54493ddf5857885a657fb89
 F src/os_win.h 41a946bea10f61c158ce8645e7646b29d44f122b
@@ -65,11 +65,11 @@ F src/printf.c cea584c5888688c650d856563aadc4296b5afac7
 F src/random.c 90adff4e73a3b249eb4f1fc2a6ff9cf78c7233a4
 F src/select.c f8a9993bcd953eb325c8c3f32985cc52b2947354
 F src/shell.c 7fb744da457b0d11e0af7f6a2f6b000fc09fe588
-F src/sqlite.h.in a3b75a6b2e66865fba4ec1b698d00c7d95fe27a2
-F src/sqliteInt.h fe9520e940c46fa6970a9cb7813b44c3f8925638
+F src/sqlite.h.in d6561d51025d08de4f455607f3f9f9aa76e855d5
+F src/sqliteInt.h f379d29ba17d7082e680c092bce0e6d4bd2d839a
 F src/table.c 25b3ff2b39b7d87e8d4a5da0713d68dfc06cbee9
-F src/tclsqlite.c e86b5483de6cb1ec1154cc5b76e3427d4b214961
-F src/test1.c 6a36fa85e9d0d4f0eaa7eadd087e40ce9cf35074
+F src/tclsqlite.c ac94682f9e601dd373912c46414a5a842db2089a
+F src/test1.c 810b7563b03efc797f350e9370955120e7072c6f
 F src/test2.c 792f203be69fea88668fa221321194f0a28dfdfa
 F src/test3.c f4e6a16a602091696619a1171bda25c0e3df49f7
 F src/test4.c a8fd681e139e1c61f22a77d07fc3a99cb28fff3f
@@ -82,8 +82,8 @@ F src/util.c 5650f6fe5ee30e0678985ad7b94da91e3f85752b
 F src/vacuum.c 829d9e1a6d7c094b80e0899686670932eafd768c
 F src/vdbe.c 69f33e22c7d0a64b23fbb69e6da95a1bb6869032
 F src/vdbe.h 3b29a9af6c7a64ed692bef1fc5f61338f40d2f67
-F src/vdbeInt.h 89a7fa5dc35477bd30ea27b0bf38e9e5c2903812
-F src/vdbeapi.c f1adebb5e3fe4724ed0e1a82c4a61809d7e15e9e
+F src/vdbeInt.h e5f2855b0f0b120d870e0459816061b88b603774
+F src/vdbeapi.c f0d36ff0f06bb5315efac5645b62e99db2c175b8
 F src/vdbeaux.c 192e0dbeaaa0bfa652b0c2579c19894e5e5626fc
 F src/vdbefifo.c 9efb94c8c3f4c979ebd0028219483f88e57584f5
 F src/vdbemem.c 4732fd4d1a75dc38549493d7f9a81d02bf7c59b5
@@ -210,13 +210,13 @@ F test/subselect.test 3f3f7a940dc3195c3139f4d530385cb54665d614
 F test/sync.test d769caaec48456119316775e35e0fdee2fa852d7
 F test/table.test d0e05ede3f6e5a8b79f8661ddcc4618cf7e69f8a
 F test/tableapi.test 6a66d58b37d46dc0f2b3c7d4bd2617d209399bd1
-F test/tclsqlite.test a8d9afe680c466881a40252a86ef0fca457ab08c
+F test/tclsqlite.test 2da3e4b3a79b13c1511c9d0cd995e08f8362e782
 F test/temptable.test 7927261befdbc7b0a7ffebb85ecc70a74fa7b15b
 F test/tester.tcl 98ecdc5723b3b2be5a8a5c3a7f38fa53031466ee
 F test/thread1.test 776c9e459b75ba905193b351926ac4019b049f35
 F test/threadtest1.c 6029d9c5567db28e6dc908a0c63099c3ba6c383b
 F test/threadtest2.c 97a830d53c24c42290501fdfba4a6e5bdd34748b
-F test/trace.test a54fa8df0d01cf827289a7659d78959e8fd2f955
+F test/trace.test 9fd28695c463b90c2d32c387a432e01eb26e8ccf
 F test/trans.test 10506dc30305cfb8c4098359f7f6f64786f69c5e
 F test/trigger1.test 152aed5a1fa90709fe171f2ca501a6b7f7901479
 F test/trigger2.test f671b922c88f70c3cd2c6f03fe7c256ae7a52fc4
@@ -299,7 +299,7 @@ F www/tclsqlite.tcl 3df553505b6efcad08f91e9b975deb2e6c9bb955
 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0
 F www/version3.tcl a99cf5f6d8bd4d5537584a2b342f0fb9fa601d8b
 F www/whentouse.tcl 97e2b5cd296f7d8057e11f44427dea8a4c2db513
-P ef84ff795c85e9d28f1cac84ff42d8d4ef84cfc4
-R 48080c33fd9c8ec18b4d4c5b999ce67c
+P 9b914901a18f8ea39c39a51509c0b3b862c13d6a
+R 96cb61bb4ce476297e665f65a9bddb8d
 U drh
-Z 3e9d07ef7774554dfa1e6c7f7ef031ab
+Z 62e9fd06364aae1ba0c7353d50daae6d
index 4dd7df2eb475df6da31e9ff92591dd954b8ad9e2..2a803f4f2064def12e3d56fec37f7a794c49d53c 100644 (file)
@@ -1 +1 @@
-9b914901a18f8ea39c39a51509c0b3b862c13d6a
\ No newline at end of file
+ed2ca0873fa89d6cfd123541d5d1c6b92c72b6ab
\ No newline at end of file
index 50ad5262d2b59590f7df520272c2f9fc87a86d0a..d79db7e8cc685a66981618cff385172a1642ade0 100644 (file)
@@ -22,7 +22,7 @@
 **     COMMIT
 **     ROLLBACK
 **
-** $Id: build.c,v 1.342 2005/08/20 03:03:04 drh Exp $
+** $Id: build.c,v 1.343 2005/08/29 23:00:04 drh Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -85,6 +85,7 @@ void sqlite3FinishCoding(Parse *pParse){
       sqlite3VdbeAddOp(v, OP_Goto, 0, pParse->cookieGoto);
     }
 
+#ifndef SQLITE_OMIT_TRACE
     /* Add a No-op that contains the complete text of the compiled SQL
     ** statement as its P3 argument.  This does not change the functionality
     ** of the program. 
@@ -92,6 +93,7 @@ void sqlite3FinishCoding(Parse *pParse){
     ** This is used to implement sqlite3_trace().
     */
     sqlite3VdbeOp3(v, OP_Noop, 0, 0, pParse->zSql, pParse->zTail-pParse->zSql);
+#endif /* SQLITE_OMIT_TRACE */
   }
 
 
index 41d1045fc5bd43f5a6135e71ecb83baaf7340208..bfc2bd597a0cda5b954cb6ae27451b6d59552f8b 100644 (file)
@@ -14,7 +14,7 @@
 ** other files are for internal use by SQLite and should not be
 ** accessed by users of the library.
 **
-** $Id: main.c,v 1.299 2005/08/28 17:00:23 drh Exp $
+** $Id: main.c,v 1.300 2005/08/29 23:00:04 drh Exp $
 */
 #include "sqliteInt.h"
 #include "os.h"
@@ -509,13 +509,14 @@ int sqlite3_create_function16(
 }
 #endif
 
+#ifndef SQLITE_OMIT_TRACE
 /*
 ** Register a trace function.  The pArg from the previously registered trace
 ** is returned.  
 **
 ** A NULL trace function means that no tracing is executes.  A non-NULL
 ** trace is a pointer to a function that is invoked at the start of each
-** sqlite3_exec().
+** SQL statement.
 */
 void *sqlite3_trace(sqlite3 *db, void (*xTrace)(void*,const char*), void *pArg){
   void *pOld = db->pTraceArg;
@@ -523,6 +524,25 @@ void *sqlite3_trace(sqlite3 *db, void (*xTrace)(void*,const char*), void *pArg){
   db->pTraceArg = pArg;
   return pOld;
 }
+/*
+** Register a profile function.  The pArg from the previously registered 
+** profile function is returned.  
+**
+** A NULL profile function means that no profiling is executes.  A non-NULL
+** profile is a pointer to a function that is invoked at the conclusion of
+** each SQL statement that is run.
+*/
+void *sqlite3_profile(
+  sqlite3 *db,
+  void (*xProfile)(void*,const char*,sqlite_uint64),
+  void *pArg
+){
+  void *pOld = db->pProfileArg;
+  db->xProfile = xProfile;
+  db->pProfileArg = pArg;
+  return pOld;
+}
+#endif /* SQLITE_OMIT_TRACE */
 
 /*** EXPERIMENTAL ***
 **
index 2ea9aa8e35f8eefbb3a1e0e64c66527436b4f774..836d01fab7b6b1df79ff35786eb35a45fa3234ea 100644 (file)
@@ -18,6 +18,7 @@
 
 
 #include <time.h>
+#include <sys/time.h>
 #include <errno.h>
 #include <unistd.h>
 
@@ -1420,9 +1421,16 @@ int sqlite3_current_time = 0;
 ** return 0.  Return 1 if the time and date cannot be found.
 */
 int sqlite3OsCurrentTime(double *prNow){
+#ifdef NO_GETTOD
   time_t t;
   time(&t);
   *prNow = t/86400.0 + 2440587.5;
+#else
+  struct timeval sNow;
+  struct timezone sTz;  /* Not used */
+  gettimeofday(&sNow, &sTz);
+  *prNow = 2440587.5 + sNow.tv_sec/86400.0 + sNow.tv_usec/86400000000.0;
+#endif
 #ifdef SQLITE_TEST
   if( sqlite3_current_time ){
     *prNow = sqlite3_current_time/86400.0 + 2440587.5;
index 3f18f88e93cdf1a37aa80a1a42d37edf5641326e..30533e4ac4d1954a56829f5dcff2dd7284bfe2b2 100644 (file)
@@ -12,7 +12,7 @@
 ** This header file defines the interface that the SQLite library
 ** presents to client programs.
 **
-** @(#) $Id: sqlite.h.in,v 1.139 2005/08/11 02:10:19 drh Exp $
+** @(#) $Id: sqlite.h.in,v 1.140 2005/08/29 23:00:04 drh Exp $
 */
 #ifndef _SQLITE3_H_
 #define _SQLITE3_H_
@@ -464,11 +464,18 @@ int sqlite3_set_authorizer(
 #define SQLITE_IGNORE 2   /* Don't allow access, but don't generate an error */
 
 /*
-** Register a function that is called at every invocation of sqlite3_exec()
-** or sqlite3_prepare().  This function can be used (for example) to generate
-** a log file of all SQL executed against a database.
+** Register a function for tracing SQL command evaluation.  The function
+** registered by sqlite3_trace() is invoked at the first sqlite3_step()
+** for the evaluation of an SQL statement.  The function registered by
+** sqlite3_profile() runs at the end of each SQL statement and includes
+** information on how long that statement ran.
+**
+** The sqlite3_profile() API is currently considered experimental and
+** is subject to change.
 */
 void *sqlite3_trace(sqlite3*, void(*xTrace)(void*,const char*), void*);
+void *sqlite3_profile(sqlite3*,
+   void(*xProfile)(void*,const char*,sqlite_uint64), void*);
 
 /*
 ** This routine configures a callback function - the progress callback - that
index dba03e1f1b256a51e0871dc5f340d2c4d42ec036..8042ae0473015528d4d24acbd3deb4f83fbf20bd 100644 (file)
@@ -11,7 +11,7 @@
 *************************************************************************
 ** Internal interface definitions for SQLite.
 **
-** @(#) $Id: sqliteInt.h,v 1.404 2005/08/28 17:00:23 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.405 2005/08/29 23:00:04 drh Exp $
 */
 #ifndef _SQLITEINT_H_
 #define _SQLITEINT_H_
@@ -420,8 +420,10 @@ struct sqlite3 {
   } init;
   struct Vdbe *pVdbe;           /* List of active virtual machines */
   int activeVdbeCnt;            /* Number of vdbes currently executing */
-  void (*xTrace)(void*,const char*);     /* Trace function */
-  void *pTraceArg;                       /* Argument to the trace function */
+  void (*xTrace)(void*,const char*);        /* Trace function */
+  void *pTraceArg;                          /* Argument to the trace function */
+  void (*xProfile)(void*,const char*,u64);  /* Profiling function */
+  void *pProfileArg;                        /* Argument to profile function */
   void *pCommitArg;             /* Argument to xCommitCallback() */   
   int (*xCommitCallback)(void*);/* Invoked at every commit. */
   void(*xCollNeeded)(void*,sqlite3*,int eTextRep,const char*);
index 6a36437d641d95557440afc17b70c36ff03388f4..5e5e6268f240f6976f41b7891d087b89df060738 100644 (file)
@@ -11,7 +11,7 @@
 *************************************************************************
 ** A TCL Interface to SQLite
 **
-** $Id: tclsqlite.c,v 1.131 2005/08/16 11:11:35 drh Exp $
+** $Id: tclsqlite.c,v 1.132 2005/08/29 23:00:04 drh Exp $
 */
 #ifndef NO_TCL     /* Omit this whole file if TCL is unavailable */
 
@@ -84,6 +84,7 @@ struct SqliteDb {
   char *zBusy;               /* The busy callback routine */
   char *zCommit;             /* The commit hook callback routine */
   char *zTrace;              /* The trace callback routine */
+  char *zProfile;            /* The profile callback routine */
   char *zProgress;           /* The progress callback routine */
   char *zAuth;               /* The authorization callback routine */
   char *zNull;               /* Text to substitute for an SQL NULL value */
@@ -190,6 +191,9 @@ static void DbDeleteCmd(void *db){
   if( pDb->zTrace ){
     Tcl_Free(pDb->zTrace);
   }
+  if( pDb->zProfile ){
+    Tcl_Free(pDb->zProfile);
+  }
   if( pDb->zAuth ){
     Tcl_Free(pDb->zAuth);
   }
@@ -247,6 +251,25 @@ static void DbTraceHandler(void *cd, const char *zSql){
   Tcl_ResetResult(pDb->interp);
 }
 
+/*
+** This routine is called by the SQLite profile handler after a statement
+** SQL has executed.  The TCL script in pDb->zProfile is evaluated.
+*/
+static void DbProfileHandler(void *cd, const char *zSql, sqlite_uint64 tm){
+  SqliteDb *pDb = (SqliteDb*)cd;
+  Tcl_DString str;
+  char zTm[100];
+
+  sqlite3_snprintf(sizeof(zTm)-1, zTm, "%lld", tm);
+  Tcl_DStringInit(&str);
+  Tcl_DStringAppend(&str, pDb->zProfile, -1);
+  Tcl_DStringAppendElement(&str, zSql);
+  Tcl_DStringAppendElement(&str, zTm);
+  Tcl_Eval(pDb->interp, Tcl_DStringValue(&str));
+  Tcl_DStringFree(&str);
+  Tcl_ResetResult(pDb->interp);
+}
+
 /*
 ** This routine is called when a transaction is committed.  The
 ** TCL script in pDb->zCommit is executed.  If it returns non-zero or
@@ -589,9 +612,10 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
     "collation_needed",   "commit_hook",       "complete",
     "copy",               "errorcode",         "eval",
     "function",           "last_insert_rowid", "nullvalue",
-    "onecolumn",          "progress",          "rekey",
-    "timeout",            "total_changes",     "trace",
-    "transaction",        "version",           0
+    "onecolumn",          "profile",           "progress",
+    "rekey",              "timeout",           "total_changes",
+    "trace",              "transaction",       "version",
+    0                    
   };
   enum DB_enum {
     DB_AUTHORIZER,        DB_BUSY,             DB_CACHE,
@@ -599,9 +623,9 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
     DB_COLLATION_NEEDED,  DB_COMMIT_HOOK,      DB_COMPLETE,
     DB_COPY,              DB_ERRORCODE,        DB_EVAL,
     DB_FUNCTION,          DB_LAST_INSERT_ROWID,DB_NULLVALUE,
-    DB_ONECOLUMN,         DB_PROGRESS,         DB_REKEY,
-    DB_TIMEOUT,           DB_TOTAL_CHANGES,    DB_TRACE,
-    DB_TRANSACTION,       DB_VERSION,          
+    DB_ONECOLUMN,         DB_PROFILE,          DB_PROGRESS,
+    DB_REKEY,             DB_TIMEOUT,          DB_TOTAL_CHANGES,
+    DB_TRACE,             DB_TRANSACTION,      DB_VERSION
   };
   /* don't leave trailing commas on DB_enum, it confuses the AIX xlc compiler */
 
@@ -780,44 +804,6 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
     break;
   }
 
-  /*    $db commit_hook ?CALLBACK?
-  **
-  ** Invoke the given callback just before committing every SQL transaction.
-  ** If the callback throws an exception or returns non-zero, then the
-  ** transaction is aborted.  If CALLBACK is an empty string, the callback
-  ** is disabled.
-  */
-  case DB_COMMIT_HOOK: {
-    if( objc>3 ){
-      Tcl_WrongNumArgs(interp, 2, objv, "?CALLBACK?");
-      return TCL_ERROR;
-    }else if( objc==2 ){
-      if( pDb->zCommit ){
-        Tcl_AppendResult(interp, pDb->zCommit, 0);
-      }
-    }else{
-      char *zCommit;
-      int len;
-      if( pDb->zCommit ){
-        Tcl_Free(pDb->zCommit);
-      }
-      zCommit = Tcl_GetStringFromObj(objv[2], &len);
-      if( zCommit && len>0 ){
-        pDb->zCommit = Tcl_Alloc( len + 1 );
-        strcpy(pDb->zCommit, zCommit);
-      }else{
-        pDb->zCommit = 0;
-      }
-      if( pDb->zCommit ){
-        pDb->interp = interp;
-        sqlite3_commit_hook(pDb->db, DbCommitHandler, pDb);
-      }else{
-        sqlite3_commit_hook(pDb->db, 0, 0);
-      }
-    }
-    break;
-  }
-
   /*
   **     $db collate NAME SCRIPT
   **
@@ -870,6 +856,44 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
     break;
   }
 
+  /*    $db commit_hook ?CALLBACK?
+  **
+  ** Invoke the given callback just before committing every SQL transaction.
+  ** If the callback throws an exception or returns non-zero, then the
+  ** transaction is aborted.  If CALLBACK is an empty string, the callback
+  ** is disabled.
+  */
+  case DB_COMMIT_HOOK: {
+    if( objc>3 ){
+      Tcl_WrongNumArgs(interp, 2, objv, "?CALLBACK?");
+      return TCL_ERROR;
+    }else if( objc==2 ){
+      if( pDb->zCommit ){
+        Tcl_AppendResult(interp, pDb->zCommit, 0);
+      }
+    }else{
+      char *zCommit;
+      int len;
+      if( pDb->zCommit ){
+        Tcl_Free(pDb->zCommit);
+      }
+      zCommit = Tcl_GetStringFromObj(objv[2], &len);
+      if( zCommit && len>0 ){
+        pDb->zCommit = Tcl_Alloc( len + 1 );
+        strcpy(pDb->zCommit, zCommit);
+      }else{
+        pDb->zCommit = 0;
+      }
+      if( pDb->zCommit ){
+        pDb->interp = interp;
+        sqlite3_commit_hook(pDb->db, DbCommitHandler, pDb);
+      }else{
+        sqlite3_commit_hook(pDb->db, 0, 0);
+      }
+    }
+    break;
+  }
+
   /*    $db complete SQL
   **
   ** Return TRUE if SQL is a complete SQL statement.  Return FALSE if
@@ -891,137 +915,323 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
     break;
   }
 
-  /*
-  **    $db errorcode
+  /*    $db copy conflict-algorithm table filename ?SEPARATOR? ?NULLINDICATOR?
   **
-  ** Return the numeric error code that was returned by the most recent
-  ** call to sqlite3_exec().
-  */
-  case DB_ERRORCODE: {
-    Tcl_SetObjResult(interp, Tcl_NewIntObj(sqlite3_errcode(pDb->db)));
-    break;
-  }
-   
-  /*
-  **    $db eval $sql ?array? ?{  ...code... }?
-  **    $db onecolumn $sql
+  ** Copy data into table from filename, optionally using SEPARATOR
+  ** as column separators.  If a column contains a null string, or the
+  ** value of NULLINDICATOR, a NULL is inserted for the column.
+  ** conflict-algorithm is one of the sqlite conflict algorithms:
+  **    rollback, abort, fail, ignore, replace
+  ** On success, return the number of lines processed, not necessarily same
+  ** as 'db changes' due to conflict-algorithm selected.
   **
-  ** 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.
-  ** If "array" and "code" are omitted, then no callback is every invoked.
-  ** If "array" is an empty string, then the values are placed in variables
-  ** that have the same name as the fields extracted by the query.
+  ** This code is basically an implementation/enhancement of
+  ** the sqlite3 shell.c ".import" command.
   **
-  ** The onecolumn method is the equivalent of:
-  **     lindex [$db eval $sql] 0
+  ** This command usage is equivalent to the sqlite2.x COPY statement,
+  ** which imports file data into a table using the PostgreSQL COPY file format:
+  **   $db copy $conflit_algo $table_name $filename \t \\N
   */
-  case DB_ONECOLUMN:
-  case DB_EVAL: {
-    char const *zSql;      /* Next SQL statement to execute */
-    char const *zLeft;     /* What is left after first stmt in zSql */
-    sqlite3_stmt *pStmt;   /* Compiled SQL statment */
-    Tcl_Obj *pArray;       /* Name of array into which results are written */
-    Tcl_Obj *pScript;      /* Script to run for each result set */
-    Tcl_Obj **apParm;      /* Parameters that need a Tcl_DecrRefCount() */
-    int nParm;             /* Number of entries used in apParm[] */
-    Tcl_Obj *aParm[10];    /* Static space for apParm[] in the common case */
-    Tcl_Obj *pRet;         /* Value to be returned */
-    SqlPreparedStmt *pPreStmt;  /* Pointer to a prepared statement */
-    int rc2;
+  case DB_COPY: {
+    char *zTable;               /* Insert data into this table */
+    char *zFile;                /* The file from which to extract data */
+    char *zConflict;            /* The conflict algorithm to use */
+    sqlite3_stmt *pStmt;        /* A statement */
+    int rc;                     /* Result code */
+    int nCol;                   /* Number of columns in the table */
+    int nByte;                  /* Number of bytes in an SQL string */
+    int i, j;                   /* Loop counters */
+    int nSep;                   /* Number of bytes in zSep[] */
+    int nNull;                  /* Number of bytes in zNull[] */
+    char *zSql;                 /* An SQL statement */
+    char *zLine;                /* A single line of input from the file */
+    char **azCol;               /* zLine[] broken up into columns */
+    char *zCommit;              /* How to commit changes */
+    FILE *in;                   /* The input file */
+    int lineno = 0;             /* Line number of input file */
+    char zLineNum[80];          /* Line number print buffer */
+    Tcl_Obj *pResult;           /* interp result */
 
-    if( choice==DB_ONECOLUMN ){
-      if( objc!=3 ){
-        Tcl_WrongNumArgs(interp, 2, objv, "SQL");
-        return TCL_ERROR;
-      }
-      pRet = 0;
+    char *zSep;
+    char *zNull;
+    if( objc<5 || objc>7 ){
+      Tcl_WrongNumArgs(interp, 2, objv, 
+         "CONFLICT-ALGORITHM TABLE FILENAME ?SEPARATOR? ?NULLINDICATOR?");
+      return TCL_ERROR;
+    }
+    if( objc>=6 ){
+      zSep = Tcl_GetStringFromObj(objv[5], 0);
     }else{
-      if( objc<3 || objc>5 ){
-        Tcl_WrongNumArgs(interp, 2, objv, "SQL ?ARRAY-NAME? ?SCRIPT?");
-        return TCL_ERROR;
-      }
-      pRet = Tcl_NewObj();
-      Tcl_IncrRefCount(pRet);
+      zSep = "\t";
     }
-    if( objc==3 ){
-      pArray = pScript = 0;
-    }else if( objc==4 ){
-      pArray = 0;
-      pScript = objv[3];
+    if( objc>=7 ){
+      zNull = Tcl_GetStringFromObj(objv[6], 0);
     }else{
-      pArray = objv[3];
-      if( Tcl_GetString(pArray)[0]==0 ) pArray = 0;
-      pScript = objv[4];
+      zNull = "";
     }
-
-    Tcl_IncrRefCount(objv[2]);
-    zSql = Tcl_GetStringFromObj(objv[2], 0);
-    while( rc==TCL_OK && zSql[0] ){
-      int i;                     /* Loop counter */
-      int nVar;                  /* Number of bind parameters in the pStmt */
-      int nCol;                  /* Number of columns in the result set */
-      Tcl_Obj **apColName = 0;   /* Array of column names */
-      int len;                   /* String length of zSql */
-  
-      /* Try to find a SQL statement that has already been compiled and
-      ** which matches the next sequence of SQL.
-      */
-      pStmt = 0;
-      pPreStmt = pDb->stmtList;
-      len = strlen(zSql);
-      if( pPreStmt && sqlite3_expired(pPreStmt->pStmt) ){
-        flushStmtCache(pDb);
-        pPreStmt = 0;
-      }
-      for(; pPreStmt; pPreStmt=pPreStmt->pNext){
-        int n = pPreStmt->nSql;
-        if( len>=n 
-            && memcmp(pPreStmt->zSql, zSql, n)==0
-            && (zSql[n]==0 || zSql[n-1]==';')
-        ){
-          pStmt = pPreStmt->pStmt;
-          zLeft = &zSql[pPreStmt->nSql];
-
-          /* When a prepared statement is found, unlink it from the
-          ** cache list.  It will later be added back to the beginning
-          ** of the cache list in order to implement LRU replacement.
-          */
-          if( pPreStmt->pPrev ){
-            pPreStmt->pPrev->pNext = pPreStmt->pNext;
-          }else{
-            pDb->stmtList = pPreStmt->pNext;
-          }
-          if( pPreStmt->pNext ){
-            pPreStmt->pNext->pPrev = pPreStmt->pPrev;
-          }else{
-            pDb->stmtLast = pPreStmt->pPrev;
-          }
-          pDb->nStmt--;
-          break;
-        }
-      }
-  
-      /* If no prepared statement was found.  Compile the SQL text
-      */
-      if( pStmt==0 ){
-        if( SQLITE_OK!=sqlite3_prepare(pDb->db, zSql, -1, &pStmt, &zLeft) ){
-          Tcl_SetObjResult(interp, dbTextToObj(sqlite3_errmsg(pDb->db)));
-          rc = TCL_ERROR;
-          break;
-        }
-        if( pStmt==0 ){
-          if( SQLITE_OK!=sqlite3_errcode(pDb->db) ){
-            /* A compile-time error in the statement
-            */
-            Tcl_SetObjResult(interp, dbTextToObj(sqlite3_errmsg(pDb->db)));
-            rc = TCL_ERROR;
-            break;
-          }else{
-            /* The statement was a no-op.  Continue to the next statement
-            ** in the SQL string.
-            */
-            zSql = zLeft;
-            continue;
+    zConflict = Tcl_GetStringFromObj(objv[2], 0);
+    zTable = Tcl_GetStringFromObj(objv[3], 0);
+    zFile = Tcl_GetStringFromObj(objv[4], 0);
+    nSep = strlen(zSep);
+    nNull = strlen(zNull);
+    if( nSep==0 ){
+      Tcl_AppendResult(interp, "Error: non-null separator required for copy", 0);
+      return TCL_ERROR;
+    }
+    if(sqlite3StrICmp(zConflict, "rollback") != 0 &&
+       sqlite3StrICmp(zConflict, "abort"   ) != 0 &&
+       sqlite3StrICmp(zConflict, "fail"    ) != 0 &&
+       sqlite3StrICmp(zConflict, "ignore"  ) != 0 &&
+       sqlite3StrICmp(zConflict, "replace" ) != 0 ) {
+      Tcl_AppendResult(interp, "Error: \"", zConflict, 
+            "\", conflict-algorithm must be one of: rollback, "
+            "abort, fail, ignore, or replace", 0);
+      return TCL_ERROR;
+    }
+    zSql = sqlite3_mprintf("SELECT * FROM '%q'", zTable);
+    if( zSql==0 ){
+      Tcl_AppendResult(interp, "Error: no such table: ", zTable, 0);
+      return TCL_ERROR;
+    }
+    nByte = strlen(zSql);
+    rc = sqlite3_prepare(pDb->db, zSql, 0, &pStmt, 0);
+    sqlite3_free(zSql);
+    if( rc ){
+      Tcl_AppendResult(interp, "Error: ", sqlite3_errmsg(pDb->db), 0);
+      nCol = 0;
+    }else{
+      nCol = sqlite3_column_count(pStmt);
+    }
+    sqlite3_finalize(pStmt);
+    if( nCol==0 ) {
+      return TCL_ERROR;
+    }
+    zSql = malloc( nByte + 50 + nCol*2 );
+    if( zSql==0 ) {
+      Tcl_AppendResult(interp, "Error: can't malloc()", 0);
+      return TCL_ERROR;
+    }
+    sqlite3_snprintf(nByte+50, zSql, "INSERT OR %q INTO '%q' VALUES(?",
+         zConflict, zTable);
+    j = strlen(zSql);
+    for(i=1; i<nCol; i++){
+      zSql[j++] = ',';
+      zSql[j++] = '?';
+    }
+    zSql[j++] = ')';
+    zSql[j] = 0;
+    rc = sqlite3_prepare(pDb->db, zSql, 0, &pStmt, 0);
+    free(zSql);
+    if( rc ){
+      Tcl_AppendResult(interp, "Error: ", sqlite3_errmsg(pDb->db), 0);
+      sqlite3_finalize(pStmt);
+      return TCL_ERROR;
+    }
+    in = fopen(zFile, "rb");
+    if( in==0 ){
+      Tcl_AppendResult(interp, "Error: cannot open file: ", zFile, NULL);
+      sqlite3_finalize(pStmt);
+      return TCL_ERROR;
+    }
+    azCol = malloc( sizeof(azCol[0])*(nCol+1) );
+    if( azCol==0 ) {
+      Tcl_AppendResult(interp, "Error: can't malloc()", 0);
+      return TCL_ERROR;
+    }
+    sqlite3_exec(pDb->db, "BEGIN", 0, 0, 0);
+    zCommit = "COMMIT";
+    while( (zLine = local_getline(0, in))!=0 ){
+      char *z;
+      i = 0;
+      lineno++;
+      azCol[0] = zLine;
+      for(i=0, z=zLine; *z; z++){
+        if( *z==zSep[0] && strncmp(z, zSep, nSep)==0 ){
+          *z = 0;
+          i++;
+          if( i<nCol ){
+            azCol[i] = &z[nSep];
+            z += nSep-1;
+          }
+        }
+      }
+      if( i+1!=nCol ){
+        char *zErr;
+        zErr = malloc(200 + strlen(zFile));
+        sprintf(zErr,"Error: %s line %d: expected %d columns of data but found %d",
+           zFile, lineno, nCol, i+1);
+        Tcl_AppendResult(interp, zErr, 0);
+        free(zErr);
+        zCommit = "ROLLBACK";
+        break;
+      }
+      for(i=0; i<nCol; i++){
+        /* check for null data, if so, bind as null */
+        if ((nNull>0 && strcmp(azCol[i], zNull)==0) || strlen(azCol[i])==0) {
+          sqlite3_bind_null(pStmt, i+1);
+        }else{
+          sqlite3_bind_text(pStmt, i+1, azCol[i], -1, SQLITE_STATIC);
+        }
+      }
+      sqlite3_step(pStmt);
+      rc = sqlite3_reset(pStmt);
+      free(zLine);
+      if( rc!=SQLITE_OK ){
+        Tcl_AppendResult(interp,"Error: ", sqlite3_errmsg(pDb->db), 0);
+        zCommit = "ROLLBACK";
+        break;
+      }
+    }
+    free(azCol);
+    fclose(in);
+    sqlite3_finalize(pStmt);
+    sqlite3_exec(pDb->db, zCommit, 0, 0, 0);
+
+    if( zCommit[0] == 'C' ){
+      /* success, set result as number of lines processed */
+      pResult = Tcl_GetObjResult(interp);
+      Tcl_SetIntObj(pResult, lineno);
+      rc = TCL_OK;
+    }else{
+      /* failure, append lineno where failed */
+      sprintf(zLineNum,"%d",lineno);
+      Tcl_AppendResult(interp,", failed while processing line: ",zLineNum,0);
+      rc = TCL_ERROR;
+    }
+    break;
+  }
+
+  /*
+  **    $db errorcode
+  **
+  ** Return the numeric error code that was returned by the most recent
+  ** call to sqlite3_exec().
+  */
+  case DB_ERRORCODE: {
+    Tcl_SetObjResult(interp, Tcl_NewIntObj(sqlite3_errcode(pDb->db)));
+    break;
+  }
+   
+  /*
+  **    $db eval $sql ?array? ?{  ...code... }?
+  **    $db onecolumn $sql
+  **
+  ** 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.
+  ** If "array" and "code" are omitted, then no callback is every invoked.
+  ** If "array" is an empty string, then the values are placed in variables
+  ** that have the same name as the fields extracted by the query.
+  **
+  ** The onecolumn method is the equivalent of:
+  **     lindex [$db eval $sql] 0
+  */
+  case DB_ONECOLUMN:
+  case DB_EVAL: {
+    char const *zSql;      /* Next SQL statement to execute */
+    char const *zLeft;     /* What is left after first stmt in zSql */
+    sqlite3_stmt *pStmt;   /* Compiled SQL statment */
+    Tcl_Obj *pArray;       /* Name of array into which results are written */
+    Tcl_Obj *pScript;      /* Script to run for each result set */
+    Tcl_Obj **apParm;      /* Parameters that need a Tcl_DecrRefCount() */
+    int nParm;             /* Number of entries used in apParm[] */
+    Tcl_Obj *aParm[10];    /* Static space for apParm[] in the common case */
+    Tcl_Obj *pRet;         /* Value to be returned */
+    SqlPreparedStmt *pPreStmt;  /* Pointer to a prepared statement */
+    int rc2;
+
+    if( choice==DB_ONECOLUMN ){
+      if( objc!=3 ){
+        Tcl_WrongNumArgs(interp, 2, objv, "SQL");
+        return TCL_ERROR;
+      }
+      pRet = 0;
+    }else{
+      if( objc<3 || objc>5 ){
+        Tcl_WrongNumArgs(interp, 2, objv, "SQL ?ARRAY-NAME? ?SCRIPT?");
+        return TCL_ERROR;
+      }
+      pRet = Tcl_NewObj();
+      Tcl_IncrRefCount(pRet);
+    }
+    if( objc==3 ){
+      pArray = pScript = 0;
+    }else if( objc==4 ){
+      pArray = 0;
+      pScript = objv[3];
+    }else{
+      pArray = objv[3];
+      if( Tcl_GetString(pArray)[0]==0 ) pArray = 0;
+      pScript = objv[4];
+    }
+
+    Tcl_IncrRefCount(objv[2]);
+    zSql = Tcl_GetStringFromObj(objv[2], 0);
+    while( rc==TCL_OK && zSql[0] ){
+      int i;                     /* Loop counter */
+      int nVar;                  /* Number of bind parameters in the pStmt */
+      int nCol;                  /* Number of columns in the result set */
+      Tcl_Obj **apColName = 0;   /* Array of column names */
+      int len;                   /* String length of zSql */
+  
+      /* Try to find a SQL statement that has already been compiled and
+      ** which matches the next sequence of SQL.
+      */
+      pStmt = 0;
+      pPreStmt = pDb->stmtList;
+      len = strlen(zSql);
+      if( pPreStmt && sqlite3_expired(pPreStmt->pStmt) ){
+        flushStmtCache(pDb);
+        pPreStmt = 0;
+      }
+      for(; pPreStmt; pPreStmt=pPreStmt->pNext){
+        int n = pPreStmt->nSql;
+        if( len>=n 
+            && memcmp(pPreStmt->zSql, zSql, n)==0
+            && (zSql[n]==0 || zSql[n-1]==';')
+        ){
+          pStmt = pPreStmt->pStmt;
+          zLeft = &zSql[pPreStmt->nSql];
+
+          /* When a prepared statement is found, unlink it from the
+          ** cache list.  It will later be added back to the beginning
+          ** of the cache list in order to implement LRU replacement.
+          */
+          if( pPreStmt->pPrev ){
+            pPreStmt->pPrev->pNext = pPreStmt->pNext;
+          }else{
+            pDb->stmtList = pPreStmt->pNext;
+          }
+          if( pPreStmt->pNext ){
+            pPreStmt->pNext->pPrev = pPreStmt->pPrev;
+          }else{
+            pDb->stmtLast = pPreStmt->pPrev;
+          }
+          pDb->nStmt--;
+          break;
+        }
+      }
+  
+      /* If no prepared statement was found.  Compile the SQL text
+      */
+      if( pStmt==0 ){
+        if( SQLITE_OK!=sqlite3_prepare(pDb->db, zSql, -1, &pStmt, &zLeft) ){
+          Tcl_SetObjResult(interp, dbTextToObj(sqlite3_errmsg(pDb->db)));
+          rc = TCL_ERROR;
+          break;
+        }
+        if( pStmt==0 ){
+          if( SQLITE_OK!=sqlite3_errcode(pDb->db) ){
+            /* A compile-time error in the statement
+            */
+            Tcl_SetObjResult(interp, dbTextToObj(sqlite3_errmsg(pDb->db)));
+            rc = TCL_ERROR;
+            break;
+          }else{
+            /* The statement was a no-op.  Continue to the next statement
+            ** in the SQL string.
+            */
+            zSql = zLeft;
+            continue;
           }
         }
         assert( pPreStmt==0 );
@@ -1301,6 +1511,37 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
     break;
   }
 
+  /*
+  **     $db nullvalue ?STRING?
+  **
+  ** Change text used when a NULL comes back from the database. If ?STRING?
+  ** is not present, then the current string used for NULL is returned.
+  ** If STRING is present, then STRING is returned.
+  **
+  */
+  case DB_NULLVALUE: {
+    if( objc!=2 && objc!=3 ){
+      Tcl_WrongNumArgs(interp, 2, objv, "NULLVALUE");
+      return TCL_ERROR;
+    }
+    if( objc==3 ){
+      int len;
+      char *zNull = Tcl_GetStringFromObj(objv[2], &len);
+      if( pDb->zNull ){
+        Tcl_Free(pDb->zNull);
+      }
+      if( zNull && len>0 ){
+        pDb->zNull = Tcl_Alloc( len + 1 );
+        strncpy(pDb->zNull, zNull, len);
+        pDb->zNull[len] = '\0';
+      }else{
+        pDb->zNull = 0;
+      }
+    }
+    Tcl_SetObjResult(interp, dbTextToObj(pDb->zNull));
+    break;
+  }
+
   /*
   **     $db last_insert_rowid 
   **
@@ -1365,6 +1606,45 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
     break;
   }
 
+  /*    $db profile ?CALLBACK?
+  **
+  ** Make arrangements to invoke the CALLBACK routine after each SQL statement
+  ** that has run.  The text of the SQL and the amount of elapse time are
+  ** appended to CALLBACK before the script is run.
+  */
+  case DB_PROFILE: {
+    if( objc>3 ){
+      Tcl_WrongNumArgs(interp, 2, objv, "?CALLBACK?");
+      return TCL_ERROR;
+    }else if( objc==2 ){
+      if( pDb->zProfile ){
+        Tcl_AppendResult(interp, pDb->zProfile, 0);
+      }
+    }else{
+      char *zProfile;
+      int len;
+      if( pDb->zProfile ){
+        Tcl_Free(pDb->zProfile);
+      }
+      zProfile = Tcl_GetStringFromObj(objv[2], &len);
+      if( zProfile && len>0 ){
+        pDb->zProfile = Tcl_Alloc( len + 1 );
+        strcpy(pDb->zProfile, zProfile);
+      }else{
+        pDb->zProfile = 0;
+      }
+#ifndef SQLITE_OMIT_TRACE
+      if( pDb->zProfile ){
+        pDb->interp = interp;
+        sqlite3_profile(pDb->db, DbProfileHandler, pDb);
+      }else{
+        sqlite3_profile(pDb->db, 0, 0);
+      }
+#endif
+    }
+    break;
+  }
+
   /*
   **     $db rekey KEY
   **
@@ -1403,37 +1683,6 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
     sqlite3_busy_timeout(pDb->db, ms);
     break;
   }
-
-  /*
-  **     $db nullvalue ?STRING?
-  **
-  ** Change text used when a NULL comes back from the database. If ?STRING?
-  ** is not present, then the current string used for NULL is returned.
-  ** If STRING is present, then STRING is returned.
-  **
-  */
-  case DB_NULLVALUE: {
-    if( objc!=2 && objc!=3 ){
-      Tcl_WrongNumArgs(interp, 2, objv, "NULLVALUE");
-      return TCL_ERROR;
-    }
-    if( objc==3 ){
-      int len;
-      char *zNull = Tcl_GetStringFromObj(objv[2], &len);
-      if( pDb->zNull ){
-        Tcl_Free(pDb->zNull);
-      }
-      if( zNull && len>0 ){
-        pDb->zNull = Tcl_Alloc( len + 1 );
-        strncpy(pDb->zNull, zNull, len);
-        pDb->zNull[len] = '\0';
-      }else{
-        pDb->zNull = 0;
-      }
-    }
-    Tcl_SetObjResult(interp, dbTextToObj(pDb->zNull));
-    break;
-  }
   
   /*
   **     $db total_changes
@@ -1479,12 +1728,14 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
       }else{
         pDb->zTrace = 0;
       }
+#ifndef SQLITE_OMIT_TRACE
       if( pDb->zTrace ){
         pDb->interp = interp;
         sqlite3_trace(pDb->db, DbTraceHandler, pDb);
       }else{
         sqlite3_trace(pDb->db, 0, 0);
       }
+#endif
     }
     break;
   }
@@ -1546,192 +1797,6 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
     break;
   }
 
-  /*    $db copy conflict-algorithm table filename ?SEPARATOR? ?NULLINDICATOR?
-  **
-  ** Copy data into table from filename, optionally using SEPARATOR
-  ** as column separators.  If a column contains a null string, or the
-  ** value of NULLINDICATOR, a NULL is inserted for the column.
-  ** conflict-algorithm is one of the sqlite conflict algorithms:
-  **    rollback, abort, fail, ignore, replace
-  ** On success, return the number of lines processed, not necessarily same
-  ** as 'db changes' due to conflict-algorithm selected.
-  **
-  ** This code is basically an implementation/enhancement of
-  ** the sqlite3 shell.c ".import" command.
-  **
-  ** This command usage is equivalent to the sqlite2.x COPY statement,
-  ** which imports file data into a table using the PostgreSQL COPY file format:
-  **   $db copy $conflit_algo $table_name $filename \t \\N
-  */
-  case DB_COPY: {
-    char *zTable;               /* Insert data into this table */
-    char *zFile;                /* The file from which to extract data */
-    char *zConflict;            /* The conflict algorithm to use */
-    sqlite3_stmt *pStmt;        /* A statement */
-    int rc;                     /* Result code */
-    int nCol;                   /* Number of columns in the table */
-    int nByte;                  /* Number of bytes in an SQL string */
-    int i, j;                   /* Loop counters */
-    int nSep;                   /* Number of bytes in zSep[] */
-    int nNull;                  /* Number of bytes in zNull[] */
-    char *zSql;                 /* An SQL statement */
-    char *zLine;                /* A single line of input from the file */
-    char **azCol;               /* zLine[] broken up into columns */
-    char *zCommit;              /* How to commit changes */
-    FILE *in;                   /* The input file */
-    int lineno = 0;             /* Line number of input file */
-    char zLineNum[80];          /* Line number print buffer */
-    Tcl_Obj *pResult;           /* interp result */
-
-    char *zSep;
-    char *zNull;
-    if( objc<5 || objc>7 ){
-      Tcl_WrongNumArgs(interp, 2, objv, 
-         "CONFLICT-ALGORITHM TABLE FILENAME ?SEPARATOR? ?NULLINDICATOR?");
-      return TCL_ERROR;
-    }
-    if( objc>=6 ){
-      zSep = Tcl_GetStringFromObj(objv[5], 0);
-    }else{
-      zSep = "\t";
-    }
-    if( objc>=7 ){
-      zNull = Tcl_GetStringFromObj(objv[6], 0);
-    }else{
-      zNull = "";
-    }
-    zConflict = Tcl_GetStringFromObj(objv[2], 0);
-    zTable = Tcl_GetStringFromObj(objv[3], 0);
-    zFile = Tcl_GetStringFromObj(objv[4], 0);
-    nSep = strlen(zSep);
-    nNull = strlen(zNull);
-    if( nSep==0 ){
-      Tcl_AppendResult(interp, "Error: non-null separator required for copy", 0);
-      return TCL_ERROR;
-    }
-    if(sqlite3StrICmp(zConflict, "rollback") != 0 &&
-       sqlite3StrICmp(zConflict, "abort"   ) != 0 &&
-       sqlite3StrICmp(zConflict, "fail"    ) != 0 &&
-       sqlite3StrICmp(zConflict, "ignore"  ) != 0 &&
-       sqlite3StrICmp(zConflict, "replace" ) != 0 ) {
-      Tcl_AppendResult(interp, "Error: \"", zConflict, 
-            "\", conflict-algorithm must be one of: rollback, "
-            "abort, fail, ignore, or replace", 0);
-      return TCL_ERROR;
-    }
-    zSql = sqlite3_mprintf("SELECT * FROM '%q'", zTable);
-    if( zSql==0 ){
-      Tcl_AppendResult(interp, "Error: no such table: ", zTable, 0);
-      return TCL_ERROR;
-    }
-    nByte = strlen(zSql);
-    rc = sqlite3_prepare(pDb->db, zSql, 0, &pStmt, 0);
-    sqlite3_free(zSql);
-    if( rc ){
-      Tcl_AppendResult(interp, "Error: ", sqlite3_errmsg(pDb->db), 0);
-      nCol = 0;
-    }else{
-      nCol = sqlite3_column_count(pStmt);
-    }
-    sqlite3_finalize(pStmt);
-    if( nCol==0 ) {
-      return TCL_ERROR;
-    }
-    zSql = malloc( nByte + 50 + nCol*2 );
-    if( zSql==0 ) {
-      Tcl_AppendResult(interp, "Error: can't malloc()", 0);
-      return TCL_ERROR;
-    }
-    sqlite3_snprintf(nByte+50, zSql, "INSERT OR %q INTO '%q' VALUES(?",
-         zConflict, zTable);
-    j = strlen(zSql);
-    for(i=1; i<nCol; i++){
-      zSql[j++] = ',';
-      zSql[j++] = '?';
-    }
-    zSql[j++] = ')';
-    zSql[j] = 0;
-    rc = sqlite3_prepare(pDb->db, zSql, 0, &pStmt, 0);
-    free(zSql);
-    if( rc ){
-      Tcl_AppendResult(interp, "Error: ", sqlite3_errmsg(pDb->db), 0);
-      sqlite3_finalize(pStmt);
-      return TCL_ERROR;
-    }
-    in = fopen(zFile, "rb");
-    if( in==0 ){
-      Tcl_AppendResult(interp, "Error: cannot open file: ", zFile, NULL);
-      sqlite3_finalize(pStmt);
-      return TCL_ERROR;
-    }
-    azCol = malloc( sizeof(azCol[0])*(nCol+1) );
-    if( azCol==0 ) {
-      Tcl_AppendResult(interp, "Error: can't malloc()", 0);
-      return TCL_ERROR;
-    }
-    sqlite3_exec(pDb->db, "BEGIN", 0, 0, 0);
-    zCommit = "COMMIT";
-    while( (zLine = local_getline(0, in))!=0 ){
-      char *z;
-      i = 0;
-      lineno++;
-      azCol[0] = zLine;
-      for(i=0, z=zLine; *z; z++){
-        if( *z==zSep[0] && strncmp(z, zSep, nSep)==0 ){
-          *z = 0;
-          i++;
-          if( i<nCol ){
-            azCol[i] = &z[nSep];
-            z += nSep-1;
-          }
-        }
-      }
-      if( i+1!=nCol ){
-        char *zErr;
-        zErr = malloc(200 + strlen(zFile));
-        sprintf(zErr,"Error: %s line %d: expected %d columns of data but found %d",
-           zFile, lineno, nCol, i+1);
-        Tcl_AppendResult(interp, zErr, 0);
-        free(zErr);
-        zCommit = "ROLLBACK";
-        break;
-      }
-      for(i=0; i<nCol; i++){
-        /* check for null data, if so, bind as null */
-        if ((nNull>0 && strcmp(azCol[i], zNull)==0) || strlen(azCol[i])==0) {
-          sqlite3_bind_null(pStmt, i+1);
-        }else{
-          sqlite3_bind_text(pStmt, i+1, azCol[i], -1, SQLITE_STATIC);
-        }
-      }
-      sqlite3_step(pStmt);
-      rc = sqlite3_reset(pStmt);
-      free(zLine);
-      if( rc!=SQLITE_OK ){
-        Tcl_AppendResult(interp,"Error: ", sqlite3_errmsg(pDb->db), 0);
-        zCommit = "ROLLBACK";
-        break;
-      }
-    }
-    free(azCol);
-    fclose(in);
-    sqlite3_finalize(pStmt);
-    sqlite3_exec(pDb->db, zCommit, 0, 0, 0);
-
-    if( zCommit[0] == 'C' ){
-      /* success, set result as number of lines processed */
-      pResult = Tcl_GetObjResult(interp);
-      Tcl_SetIntObj(pResult, lineno);
-      rc = TCL_OK;
-    }else{
-      /* failure, append lineno where failed */
-      sprintf(zLineNum,"%d",lineno);
-      Tcl_AppendResult(interp,", failed while processing line: ",zLineNum,0);
-      rc = TCL_ERROR;
-    }
-    break;
-  }
-
   /*    $db version
   **
   ** Return the version string for this database.
index e181f09e10004a043cad371d0ebba4d84804cd09..cd0c65585cf15958c18da4a2e6f298041fae6162 100644 (file)
@@ -13,7 +13,7 @@
 ** is not included in the SQLite library.  It is used for automated
 ** testing of the SQLite library.
 **
-** $Id: test1.c,v 1.155 2005/08/19 00:14:42 drh Exp $
+** $Id: test1.c,v 1.156 2005/08/29 23:00:05 drh Exp $
 */
 #include "sqliteInt.h"
 #include "tcl.h"
@@ -2954,6 +2954,12 @@ static void set_options(Tcl_Interp *interp){
   Tcl_SetVar2(interp, "sqlite_options", "threadsafe", "0", TCL_GLOBAL_ONLY);
 #endif
 
+#ifdef SQLITE_OMIT_TRACE
+  Tcl_SetVar2(interp, "sqlite_options", "trace", "0", TCL_GLOBAL_ONLY);
+#else
+  Tcl_SetVar2(interp, "sqlite_options", "trace", "1", TCL_GLOBAL_ONLY);
+#endif
+
 #ifdef SQLITE_OMIT_TRIGGER
   Tcl_SetVar2(interp, "sqlite_options", "trigger", "0", TCL_GLOBAL_ONLY);
 #else
index 7bf2a0ec5dc4c7c5bb30588504a40555a9129f1f..34e8d230be9cb01e9de9fd731e571f7600b889d3 100644 (file)
@@ -358,6 +358,7 @@ struct Vdbe {
   u8 aborted;             /* True if ROLLBACK in another VM causes an abort */
   u8 expired;             /* True if the VM needs to be recompiled */
   int nChange;            /* Number of db changes made since last reset */
+  u64 startTime;          /* Time when query started - used for profiling */
 };
 
 /*
index dfe8f12c418e6df091ce554317fe89f857f623c3..75af8a9b65d0c575b7da9046a772dcb3ede34ba1 100644 (file)
@@ -15,6 +15,7 @@
 */
 #include "sqliteInt.h"
 #include "vdbeInt.h"
+#include "os.h"
 
 /*
 ** Return TRUE (non-zero) of the statement supplied as an argument needs
@@ -173,9 +174,10 @@ int sqlite3_step(sqlite3_stmt *pStmt){
     return SQLITE_MISUSE;
   }
   if( p->pc<0 ){
+#ifndef SQLITE_OMIT_TRACE
     /* Invoke the trace callback if there is one
     */
-    if( (db = p->db)->xTrace && !db->init.busy ){
+    if( db->xTrace && !db->init.busy ){
       assert( p->nOp>0 );
       assert( p->aOp[p->nOp-1].opcode==OP_Noop );
       assert( p->aOp[p->nOp-1].p3!=0 );
@@ -187,6 +189,12 @@ int sqlite3_step(sqlite3_stmt *pStmt){
         return SQLITE_MISUSE;
       }
     }
+    if( db->xProfile && !db->init.busy ){
+      double rNow;
+      sqlite3OsCurrentTime(&rNow);
+      p->startTime = (rNow - (int)rNow)*3600.0*24.0*1000000000.0;
+    }
+#endif
 
     /* Print a copy of SQL as it is executed if the SQL_TRACE pragma is turned
     ** on in debugging mode.
@@ -213,6 +221,23 @@ int sqlite3_step(sqlite3_stmt *pStmt){
     rc = SQLITE_MISUSE;
   }
 
+#ifndef SQLITE_OMIT_TRACE
+  /* Invoke the profile callback if there is one
+  */
+  if( rc!=SQLITE_ROW && db->xProfile && !db->init.busy ){
+    double rNow;
+    u64 elapseTime;
+
+    sqlite3OsCurrentTime(&rNow);
+    elapseTime = (rNow - (int)rNow)*3600.0*24.0*1000000000.0 - p->startTime;
+    assert( p->nOp>0 );
+    assert( p->aOp[p->nOp-1].opcode==OP_Noop );
+    assert( p->aOp[p->nOp-1].p3!=0 );
+    assert( p->aOp[p->nOp-1].p3type==P3_DYNAMIC );
+    db->xProfile(db->pProfileArg, p->aOp[p->nOp-1].p3, elapseTime);
+  }
+#endif
+
   sqlite3Error(p->db, rc, p->zErrMsg ? "%s" : 0, p->zErrMsg);
   return rc;
 }
index fd52b245df585982129d927d05860524e09d6250..9de166915e9fd6d758afc2bdc278d775a1f58837 100644 (file)
@@ -15,7 +15,7 @@
 # interface is pretty well tested.  This file contains some addition
 # tests for fringe issues that the main test suite does not cover.
 #
-# $Id: tclsqlite.test,v 1.43 2005/08/02 17:15:16 drh Exp $
+# $Id: tclsqlite.test,v 1.44 2005/08/29 23:00:05 drh Exp $
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
@@ -34,7 +34,7 @@ do_test tcl-1.1 {
 do_test tcl-1.2 {
   set v [catch {db bogus} msg]
   lappend v $msg
-} {1 {bad option "bogus": must be authorizer, busy, cache, changes, close, collate, collation_needed, commit_hook, complete, copy, errorcode, eval, function, last_insert_rowid, nullvalue, onecolumn, progress, rekey, timeout, total_changes, trace, transaction, or version}}
+} {1 {bad option "bogus": must be authorizer, busy, cache, changes, close, collate, collation_needed, commit_hook, complete, copy, errorcode, eval, function, last_insert_rowid, nullvalue, onecolumn, profile, progress, rekey, timeout, total_changes, trace, transaction, or version}}
 do_test tcl-1.3 {
   execsql {CREATE TABLE t1(a int, b int)}
   execsql {INSERT INTO t1 VALUES(10,20)}
index ea1756076a3eb51e879ceaf0759488c5d0df0d1d..da81904ec7ee5d5c204dc7490516b829dfd95a71 100644 (file)
 #
 # This file implements tests for the "sqlite3_trace()" API.
 #
-# $Id: trace.test,v 1.4 2004/09/17 20:46:55 drh Exp $
+# $Id: trace.test,v 1.5 2005/08/29 23:00:05 drh Exp $
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
 
+ifcapable !trace {
+  finish_test
+  return
+}
+
 set ::stmtlist {}
 do_test trace-1.1 {
   set rc [catch {db trace 1 2 3} msg]
@@ -77,5 +82,67 @@ do_test trace-2.5 {
 } {SELECT * FROM t1}
 catch {sqlite3_finalize $STMT}
 
+# Similar tests, but this time for profiling.
+# 
+do_test trace-3.1 {
+  set rc [catch {db profile 1 2 3} msg]
+  lappend rc $msg
+} {1 {wrong # args: should be "db profile ?CALLBACK?"}}
+set ::stmtlist {}
+proc profile_proc {cmd tm} {
+  lappend ::stmtlist [string trim $cmd]
+}
+do_test trace-3.2 {
+  db trace {}
+  db profile profile_proc
+  db profile
+} {profile_proc}
+do_test trace-3.3 {
+  execsql {
+    CREATE TABLE t2(a,b);
+    INSERT INTO t2 VALUES(1,2);
+    SELECT * FROM t2;
+  }
+} {1 2}
+do_test trace-3.4 {
+  set ::stmtlist
+} {{CREATE TABLE t2(a,b);} {INSERT INTO t2 VALUES(1,2);} {SELECT * FROM t2;}}
+do_test trace-3.5 {
+  db profile {}
+  db profile
+} {}
+
+# If we prepare a statement and execute it multiple times, the profile
+# happens on each execution.
+#
+db close
+set DB [sqlite3 db test.db]
+do_test trace-4.1 {
+  set STMT [sqlite3_prepare $DB {INSERT INTO t2 VALUES(2,3)} -1 TAIL]
+  db trace trace_proc
+  proc profile_proc {sql tm} {
+    global TRACE_OUT
+    set TRACE_OUT $sql
+  }
+  set TRACE_OUT {}
+  sqlite3_step $STMT
+  set TRACE_OUT
+} {INSERT INTO t2 VALUES(2,3)}
+do_test trace-4.2 {
+  set TRACE_OUT {}
+  sqlite3_reset $STMT
+  set TRACE_OUT 
+} {}
+do_test trace-4.3 {
+  sqlite3_step $STMT
+  set TRACE_OUT
+} {INSERT INTO t2 VALUES(2,3)}
+do_test trace-4.4 {
+  execsql {SELECT * FROM t1}
+} {1 2 2 3 2 3}
+do_test trace-4.5 {
+  set TRACE_OUT
+} {SELECT * FROM t1}
+catch {sqlite3_finalize $STMT}
 
 finish_test