aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--manifest32
-rw-r--r--manifest.uuid2
-rw-r--r--src/build.c4
-rw-r--r--src/main.c24
-rw-r--r--src/os_unix.c8
-rw-r--r--src/sqlite.h.in15
-rw-r--r--src/sqliteInt.h8
-rw-r--r--src/tclsqlite.c589
-rw-r--r--src/test1.c8
-rw-r--r--src/vdbeInt.h1
-rw-r--r--src/vdbeapi.c27
-rw-r--r--test/tclsqlite.test4
-rw-r--r--test/trace.test69
13 files changed, 497 insertions, 294 deletions
diff --git a/manifest b/manifest
index 799cedf05..5ecf55d30 100644
--- 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
diff --git a/manifest.uuid b/manifest.uuid
index 4dd7df2eb..2a803f4f2 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-9b914901a18f8ea39c39a51509c0b3b862c13d6a \ No newline at end of file
+ed2ca0873fa89d6cfd123541d5d1c6b92c72b6ab \ No newline at end of file
diff --git a/src/build.c b/src/build.c
index 50ad5262d..d79db7e8c 100644
--- a/src/build.c
+++ b/src/build.c
@@ -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 */
}
diff --git a/src/main.c b/src/main.c
index 41d1045fc..bfc2bd597 100644
--- a/src/main.c
+++ b/src/main.c
@@ -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 ***
**
diff --git a/src/os_unix.c b/src/os_unix.c
index 2ea9aa8e3..836d01fab 100644
--- a/src/os_unix.c
+++ b/src/os_unix.c
@@ -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;
diff --git a/src/sqlite.h.in b/src/sqlite.h.in
index 3f18f88e9..30533e4ac 100644
--- a/src/sqlite.h.in
+++ b/src/sqlite.h.in
@@ -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
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index dba03e1f1..8042ae047 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -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*);
diff --git a/src/tclsqlite.c b/src/tclsqlite.c
index 6a36437d6..5e5e6268f 100644
--- a/src/tclsqlite.c
+++ b/src/tclsqlite.c
@@ -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);
}
@@ -248,6 +252,25 @@ static void DbTraceHandler(void *cd, const char *zSql){
}
/*
+** 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
** if it throws an exception, the transaction is rolled back instead
@@ -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,6 +915,192 @@ 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 errorcode
**
@@ -1302,6 +1512,37 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
}
/*
+ ** $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
**
** Return an integer which is the ROWID for the most recent insert.
@@ -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.
diff --git a/src/test1.c b/src/test1.c
index e181f09e1..cd0c65585 100644
--- a/src/test1.c
+++ b/src/test1.c
@@ -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
diff --git a/src/vdbeInt.h b/src/vdbeInt.h
index 7bf2a0ec5..34e8d230b 100644
--- a/src/vdbeInt.h
+++ b/src/vdbeInt.h
@@ -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 */
};
/*
diff --git a/src/vdbeapi.c b/src/vdbeapi.c
index dfe8f12c4..75af8a9b6 100644
--- a/src/vdbeapi.c
+++ b/src/vdbeapi.c
@@ -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;
}
diff --git a/test/tclsqlite.test b/test/tclsqlite.test
index fd52b245d..9de166915 100644
--- a/test/tclsqlite.test
+++ b/test/tclsqlite.test
@@ -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)}
diff --git a/test/trace.test b/test/trace.test
index ea1756076..da81904ec 100644
--- a/test/trace.test
+++ b/test/trace.test
@@ -12,11 +12,16 @@
#
# 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