Increase resolution of time-of-day on unix. Add an experimental
sqlite3_profile() API. (CVS 2639) FossilOrigin-Name: ed2ca0873fa89d6cfd123541d5d1c6b92c72b6ab
This commit is contained in:
parent
b46b57745d
commit
19e2d37f1d
32
manifest
32
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
|
||||
|
@ -1 +1 @@
|
||||
9b914901a18f8ea39c39a51509c0b3b862c13d6a
|
||||
ed2ca0873fa89d6cfd123541d5d1c6b92c72b6ab
|
@ -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 */
|
||||
}
|
||||
|
||||
|
||||
|
24
src/main.c
24
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 ***
|
||||
**
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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*);
|
||||
|
589
src/tclsqlite.c
589
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);
|
||||
}
|
||||
@ -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,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
|
||||
**
|
||||
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user