Add the sqlite3_expanded_sql() and sqlite3_trace_v2() interfaces.
FossilOrigin-Name: 99a6c51887d9d78409944c5a028d5a3ac2e066a7
This commit is contained in:
commit
194c21de34
38
manifest
38
manifest
@ -1,5 +1,5 @@
|
||||
C Revise\sa\swarning\sfix\sfrom\sthe\sprevious\scheck-in\sto\simprove\sclarity.\s\sAlso,\sfix\san\sincorrect\stest\sprefix\s(typo).
|
||||
D 2016-07-22T21:35:38.430
|
||||
C Add\sthe\ssqlite3_expanded_sql()\sand\ssqlite3_trace_v2()\sinterfaces.
|
||||
D 2016-07-23T05:22:02.142
|
||||
F Makefile.in 6c20d44f72d4564f11652b26291a214c8367e5db
|
||||
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
|
||||
F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a
|
||||
@ -348,7 +348,7 @@ F src/hwtime.h 747c1bbe9df21a92e9c50f3bbec1de841dc5e5da
|
||||
F src/insert.c 8f4e9fcbd8e95e85f15647ba8b413b18d556ec2b
|
||||
F src/legacy.c 75d3023be8f0d2b99d60f905090341a03358c58e
|
||||
F src/loadext.c 4237fd37ca589f1d90b3ea925dd9df20da8e0c84
|
||||
F src/main.c 405d13e3a4f7c5add9fb27702ae70ed0a6e32cca
|
||||
F src/main.c 2d3e62a971e7f169a74448e81af9ebde95b07381
|
||||
F src/malloc.c 1443d1ad95d67c21d77af7ae3f44678252f0efec
|
||||
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
|
||||
F src/mem1.c 6919bcf12f221868ea066eec27e579fed95ce98b
|
||||
@ -385,15 +385,15 @@ F src/resolve.c cca3aa77b95706df5d635a2141a4d1de60ae6598
|
||||
F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac
|
||||
F src/select.c f3c6e9065fb34f6a23af27ec7f1f717ffbfc2ee4
|
||||
F src/shell.c a8a9e392a6a2777fabf5feb536931cb190f235e5
|
||||
F src/sqlite.h.in b9ba728c1083b7a8ab5f6a628b25cd2a00325fbf
|
||||
F src/sqlite.h.in 3dc7317ded4bf66facd845705f45f21d03517940
|
||||
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
||||
F src/sqlite3ext.h 2a170163d121095c6ab1ef05ed0413722f391d01
|
||||
F src/sqliteInt.h dcf43b8abc5605b70f54ba80f42b6ad054b8ba95
|
||||
F src/sqlite3ext.h 46f300b6e300e0fa916d7d58c44b53415b8471a9
|
||||
F src/sqliteInt.h 48cd97eb134665348393dfe277b4c14d1085bfc7
|
||||
F src/sqliteLimit.h c0373387c287c8d0932510b5547ecde31b5da247
|
||||
F src/status.c 5b18f9526900f61189ab0b83f1ef41d9f871a2ab
|
||||
F src/table.c 5226df15ab9179b9ed558d89575ea0ce37b03fc9
|
||||
F src/tclsqlite.c 25fbbbb97f76dbfd113153fb63f52d7ecfac5dd0
|
||||
F src/test1.c 640f862c490c8eee09a9d02d0d128c6a8a75336d
|
||||
F src/tclsqlite.c 573e63c959b314d77486f3565fa79c60cda3df7e
|
||||
F src/test1.c 186e3b53c402b7a73bcb4ade2b77709675c39fe3
|
||||
F src/test2.c 5586f43fcd9a1be0830793cf9d354082c261b25b
|
||||
F src/test3.c c75c8af0eadb335236c9e61b51044c58a8f7dd59
|
||||
F src/test4.c d168f83cc78d02e8d35567bb5630e40dcd85ac1e
|
||||
@ -448,16 +448,16 @@ F src/trigger.c e14840ee0c3e549e758ec9bf3e4146e166002280
|
||||
F src/update.c 4f05ea8cddfa367d045e03589756c02199e8f9bd
|
||||
F src/utf.c 699001c79f28e48e9bcdf8a463da029ea660540c
|
||||
F src/util.c 810ec3f22e2d1b62e66c30fe3621ebdedd23584d
|
||||
F src/vacuum.c feb1eabb20987983d9350cad98299b21fa811f52
|
||||
F src/vdbe.c 22b46c3b725e950e9f2760e2d76953d592600ad4
|
||||
F src/vacuum.c 544ec90a66a4b0460b7c1baeadbc21b45e6ca0b6
|
||||
F src/vdbe.c 7b5570f77e32766949fd8f9478e4a9bb06e6ab24
|
||||
F src/vdbe.h 67bc551f7faf04c33493892e4b378aada823ed10
|
||||
F src/vdbeInt.h c59381049af5c7751a83456c39b80d1a6fde1f9d
|
||||
F src/vdbeapi.c 02bcbc2ca5d2004b029088b05b468b394881e103
|
||||
F src/vdbeaux.c c90275b0e55a2b32c03dc09314194fe46f2429d8
|
||||
F src/vdbeapi.c c3f6715a99995c11748ecad91d25e93fd9fc390b
|
||||
F src/vdbeaux.c a32d79aeaa88dc2b97c261172d952d395254a055
|
||||
F src/vdbeblob.c 83d2d266383157b02e2b809350bb197e89d7895b
|
||||
F src/vdbemem.c 1ecaa5ee0caff07255f25d04e8dc88befb6f88d1
|
||||
F src/vdbesort.c 91fda3909326860382b0ca8aa251e609c6a9d62c
|
||||
F src/vdbetrace.c f75c5455d8cf389ef86a8bfdfd3177e0e3692484
|
||||
F src/vdbetrace.c 41963d5376f0349842b5fc4aaaaacd7d9cdc0834
|
||||
F src/vtab.c 948d2d4984219eee37a7bf427d6667e21e6eb92e
|
||||
F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
|
||||
F src/wal.c 02eeecc265f6ffd0597378f5d8ae9070b62a406a
|
||||
@ -1120,7 +1120,7 @@ F test/tabfunc01.test 50a9fb379f9747fd0d40ea6d8fa3a101361bb537
|
||||
F test/table.test b708f3e5fa2542fa51dfab21fc07b36ea445cb2f
|
||||
F test/tableapi.test 2674633fa95d80da917571ebdd759a14d9819126
|
||||
F test/tableopts.test dba698ba97251017b7c80d738c198d39ab747930
|
||||
F test/tclsqlite.test cf0d0a3fd03d64892cec2d48aae9fb2f148680a5
|
||||
F test/tclsqlite.test 1d73b9203b1ca8798d7d7310742b8d3febc0d56e
|
||||
F test/tempdb.test bd92eba8f20e16a9136e434e20b280794de3cdb6
|
||||
F test/tempdb2.test 4fc92055f2a3f7626c0f2eabbb637dc021b311d5
|
||||
F test/tempfault.test 0c0d349c9a99bf5f374655742577f8712c647900
|
||||
@ -1285,6 +1285,7 @@ F test/tokenize.test ce430a7aed48fc98301611429595883fdfcab5d7
|
||||
F test/tpch01.test 04adbf8d8300fa60a222f28d901abd76e7be6dd4
|
||||
F test/trace.test 6f676313e3ebd2a50585036d2f212a3319dd5836
|
||||
F test/trace2.test f5cb67ad3bc09e0c58e8cca78dfd0b5639259983
|
||||
F test/trace3.test 01e4111d582c7b20ab1c63156169157d256bc3d5
|
||||
F test/trans.test 6e1b4c6a42dba31bd65f8fa5e61a2708e08ddde6
|
||||
F test/trans2.test 62bd045bfc7a1c14c5ba83ba64d21ade31583f76
|
||||
F test/trans3.test 91a100e5412b488e22a655fe423a14c26403ab94
|
||||
@ -1506,7 +1507,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||
P 8bb6e6fcedf7d0ae796b8848593106c921ebf21a
|
||||
R 5b6ff2c3db1a5eba26e3cea3eedea20b
|
||||
U mistachkin
|
||||
Z 074e5835acae805d82010c066036788b
|
||||
P f50a3fd6606c14b82c9b938bfca284d54b6c650f 0400f642d542e62bb428e0bf263964c65691368e
|
||||
R 4a3af12ad9efd4d7dc81ee4ca45f95f6
|
||||
T +closed 0400f642d542e62bb428e0bf263964c65691368e
|
||||
U drh
|
||||
Z 2820f9890a85b78d1796850ebe9bb3ba
|
||||
|
@ -1 +1 @@
|
||||
f50a3fd6606c14b82c9b938bfca284d54b6c650f
|
||||
99a6c51887d9d78409944c5a028d5a3ac2e066a7
|
32
src/main.c
32
src/main.c
@ -1033,6 +1033,9 @@ static int sqlite3Close(sqlite3 *db, int forceZombie){
|
||||
return SQLITE_MISUSE_BKPT;
|
||||
}
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
if( db->mTrace & SQLITE_TRACE_CLOSE ){
|
||||
db->xTrace(SQLITE_TRACE_CLOSE, db->pTraceArg, db, 0);
|
||||
}
|
||||
|
||||
/* Force xDisconnect calls on all virtual tables */
|
||||
disconnectAllVtab(db);
|
||||
@ -1801,7 +1804,8 @@ int sqlite3_overload_function(
|
||||
** trace is a pointer to a function that is invoked at the start of each
|
||||
** SQL statement.
|
||||
*/
|
||||
void *sqlite3_trace(sqlite3 *db, void (*xTrace)(void*,const char*), void *pArg){
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
void *sqlite3_trace(sqlite3 *db, void(*xTrace)(void*,const char*), void *pArg){
|
||||
void *pOld;
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
@ -1812,11 +1816,35 @@ void *sqlite3_trace(sqlite3 *db, void (*xTrace)(void*,const char*), void *pArg){
|
||||
#endif
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
pOld = db->pTraceArg;
|
||||
db->xTrace = xTrace;
|
||||
db->mTrace = xTrace ? SQLITE_TRACE_LEGACY : 0;
|
||||
db->xTrace = (int(*)(u32,void*,void*,void*))xTrace;
|
||||
db->pTraceArg = pArg;
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return pOld;
|
||||
}
|
||||
#endif /* SQLITE_OMIT_DEPRECATED */
|
||||
|
||||
/* Register a trace callback using the version-2 interface.
|
||||
*/
|
||||
int sqlite3_trace_v2(
|
||||
sqlite3 *db, /* Trace this connection */
|
||||
unsigned mTrace, /* Mask of events to be traced */
|
||||
int(*xTrace)(unsigned,void*,void*,void*), /* Callback to invoke */
|
||||
void *pArg /* Context */
|
||||
){
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) ){
|
||||
return SQLITE_MISUSE_BKPT;
|
||||
}
|
||||
#endif
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
db->mTrace = mTrace;
|
||||
db->xTrace = xTrace;
|
||||
db->pTraceArg = pArg;
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Register a profile function. The pArg from the previously registered
|
||||
** profile function is returned.
|
||||
|
128
src/sqlite.h.in
128
src/sqlite.h.in
@ -2755,6 +2755,9 @@ int sqlite3_set_authorizer(
|
||||
** CAPI3REF: Tracing And Profiling Functions
|
||||
** METHOD: sqlite3
|
||||
**
|
||||
** These routines are deprecated. Use the [sqlite3_trace_v2()] interface
|
||||
** instead of the routines described here.
|
||||
**
|
||||
** These routines register callback functions that can be used for
|
||||
** tracing and profiling the execution of SQL statements.
|
||||
**
|
||||
@ -2780,10 +2783,101 @@ int sqlite3_set_authorizer(
|
||||
** sqlite3_profile() function is considered experimental and is
|
||||
** subject to change in future versions of SQLite.
|
||||
*/
|
||||
void *sqlite3_trace(sqlite3*, void(*xTrace)(void*,const char*), void*);
|
||||
SQLITE_EXPERIMENTAL void *sqlite3_profile(sqlite3*,
|
||||
SQLITE_DEPRECATED void *sqlite3_trace(sqlite3*,
|
||||
void(*xTrace)(void*,const char*), void*);
|
||||
SQLITE_DEPRECATED void *sqlite3_profile(sqlite3*,
|
||||
void(*xProfile)(void*,const char*,sqlite3_uint64), void*);
|
||||
|
||||
/*
|
||||
** CAPI3REF: SQL Trace Event Codes
|
||||
** KEYWORDS: SQLITE_TRACE
|
||||
**
|
||||
** These constants identify classes of events that can be monitored
|
||||
** using the [sqlite3_trace_v2()] tracing logic. The third argument
|
||||
** to [sqlite3_trace_v2()] is an OR-ed combination of one or more of
|
||||
** the following constants. ^The first argument to the trace callback
|
||||
** is one of the following constants.
|
||||
**
|
||||
** New tracing constants may be added in future releases.
|
||||
**
|
||||
** ^A trace callback has four arguments: xCallback(T,C,P,X).
|
||||
** ^The T argument is one of the integer type codes above.
|
||||
** ^The C argument is a copy of the context pointer passed in as the
|
||||
** fourth argument to [sqlite3_trace_v2()].
|
||||
** The P and X arguments are pointers whose meanings depend on T.
|
||||
**
|
||||
** <dl>
|
||||
** [[SQLITE_TRACE_STMT]] <dt>SQLITE_TRACE_STMT</dt>
|
||||
** <dd>^An SQLITE_TRACE_STMT callback is invoked when a prepared statement
|
||||
** first begins running and possibly at other times during the
|
||||
** execution of the prepared statement, such as at the start of each
|
||||
** trigger subprogram. ^The P argument is a pointer to the
|
||||
** [prepared statement]. ^The X argument is a pointer to a string which
|
||||
** is the expanded SQL text of the prepared statement or a comment that
|
||||
** indicates the invocation of a trigger.
|
||||
**
|
||||
** [[SQLITE_TRACE_PROFILE]] <dt>SQLITE_TRACE_PROFILE</dt>
|
||||
** <dd>^An SQLITE_TRACE_PROFILE callback provides approximately the same
|
||||
** information as is provided by the [sqlite3_profile()] callback.
|
||||
** ^The P argument is a pointer to the [prepared statement] and the
|
||||
** X argument points to a 64-bit integer which is the estimated of
|
||||
** the number of nanosecond that the prepared statement took to run.
|
||||
** ^The SQLITE_TRACE_PROFILE callback is invoked when the statement finishes.
|
||||
**
|
||||
** [[SQLITE_TRACE_ROW]] <dt>SQLITE_TRACE_ROW</dt>
|
||||
** <dd>^An SQLITE_TRACE_ROW callback is invoked whenever a prepared
|
||||
** statement generates a single row of result.
|
||||
** ^The P argument is a pointer to the [prepared statement] and the
|
||||
** X argument is unused.
|
||||
**
|
||||
** [[SQLITE_TRACE_CLOSE]] <dt>SQLITE_TRACE_CLOSE</dt>
|
||||
** <dd>^An SQLITE_TRACE_CLOSE callback is invoked when a database
|
||||
** connection closes.
|
||||
** ^The P argument is a pointer to the [database connection] object
|
||||
** and the X argument is unused.
|
||||
** </dl>
|
||||
*/
|
||||
#define SQLITE_TRACE_STMT 0x01
|
||||
#define SQLITE_TRACE_PROFILE 0x02
|
||||
#define SQLITE_TRACE_ROW 0x04
|
||||
#define SQLITE_TRACE_CLOSE 0x08
|
||||
|
||||
/*
|
||||
** CAPI3REF: SQL Trace Hook
|
||||
** METHOD: sqlite3
|
||||
**
|
||||
** ^The sqlite3_trace_v2(D,M,X,P) interface registers a trace callback
|
||||
** function X against [database connection] D, using property mask M
|
||||
** and context pointer P. ^If the X callback is
|
||||
** NULL or if the M mask is zero, then tracing is disabled. The
|
||||
** M argument should be the bitwise OR-ed combination of
|
||||
** zero or more [SQLITE_TRACE] constants.
|
||||
**
|
||||
** ^Each call to either sqlite3_trace() or sqlite3_trace_v2() overrides
|
||||
** (cancels) any prior calls to sqlite3_trace() or sqlite3_trace_v2().
|
||||
**
|
||||
** ^The X callback is invoked whenever any of the events identified by
|
||||
** mask M occur. ^The integer return value from the callback is currently
|
||||
** ignored, though this may change in future releases. Callback
|
||||
** implementations should return zero to ensure future compatibility.
|
||||
**
|
||||
** ^A trace callback is invoked with four arguments: callback(T,C,P,X).
|
||||
** ^The T argument is one of the [SQLITE_TRACE]
|
||||
** constants to indicate why the callback was invoked.
|
||||
** ^The C argument is a copy of the context pointer.
|
||||
** The P and X arguments are pointers whose meanings depend on T.
|
||||
**
|
||||
** The sqlite3_trace_v2() interface is intended to replace the legacy
|
||||
** interfaces [sqlite3_trace()] and [sqlite3_profile()], both of which
|
||||
** are deprecated.
|
||||
*/
|
||||
int sqlite3_trace_v2(
|
||||
sqlite3*,
|
||||
unsigned uMask,
|
||||
int(*xCallback)(unsigned,void*,void*,void*),
|
||||
void *pCtx
|
||||
);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Query Progress Callbacks
|
||||
** METHOD: sqlite3
|
||||
@ -3402,11 +3496,35 @@ int sqlite3_prepare16_v2(
|
||||
** CAPI3REF: Retrieving Statement SQL
|
||||
** METHOD: sqlite3_stmt
|
||||
**
|
||||
** ^This interface can be used to retrieve a saved copy of the original
|
||||
** SQL text used to create a [prepared statement] if that statement was
|
||||
** compiled using either [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()].
|
||||
** ^The sqlite3_sql(P) interface returns a pointer to a copy of the UTF-8
|
||||
** SQL text used to create [prepared statement] P if P was
|
||||
** created by either [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()].
|
||||
** ^The sqlite3_expanded_sql(P) interface returns a pointer to a UTF-8
|
||||
** string containing the SQL text of prepared statement P with
|
||||
** [bound parameters] expanded.
|
||||
**
|
||||
** ^(For example, if a prepared statement is created using the SQL
|
||||
** text "SELECT $abc,:xyz" and if parameter $abc is bound to integer 2345
|
||||
** and parameter :xyz is unbound, then sqlite3_sql() will return
|
||||
** the original string, "SELECT $abc,:xyz" but sqlite3_expanded_sql()
|
||||
** will return "SELECT 2345,NULL".)^
|
||||
**
|
||||
** ^The sqlite3_expanded_sql() interface returns NULL if insufficient memory
|
||||
** is available to hold the result, or if the result would exceed the
|
||||
** the maximum string length determined by the [SQLITE_LIMIT_LENGTH].
|
||||
**
|
||||
** ^The [SQLITE_TRACE_SIZE_LIMIT] compile-time option limits the size of
|
||||
** bound parameter expansions. ^The [SQLITE_OMIT_TRACE] compile-time
|
||||
** option causes sqlite3_expanded_sql() to always return NULL.
|
||||
**
|
||||
** ^The string returned by sqlite3_sql(P) is managed by SQLite and is
|
||||
** automatically freed when the prepared statement is finalized.
|
||||
** ^The string returned by sqlite3_expanded_sql(P), on the other hand,
|
||||
** is obtained from [sqlite3_malloc()] and must be free by the application
|
||||
** by passing it to [sqlite3_free()].
|
||||
*/
|
||||
const char *sqlite3_sql(sqlite3_stmt *pStmt);
|
||||
char *sqlite3_expanded_sql(sqlite3_stmt *pStmt);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Determine If An SQL Statement Writes The Database
|
||||
|
@ -281,6 +281,9 @@ struct sqlite3_api_routines {
|
||||
int (*db_cacheflush)(sqlite3*);
|
||||
/* Version 3.12.0 and later */
|
||||
int (*system_errno)(sqlite3*);
|
||||
/* Version 3.14.0 and later */
|
||||
int (*trace_v2)(sqlite3*,unsigned,int(*)(unsigned,void*,void*,void*),void*);
|
||||
char *(*expanded_sql)(sqlite3_stmt*);
|
||||
};
|
||||
|
||||
/*
|
||||
@ -526,6 +529,9 @@ struct sqlite3_api_routines {
|
||||
#define sqlite3_db_cacheflush sqlite3_api->db_cacheflush
|
||||
/* Version 3.12.0 and later */
|
||||
#define sqlite3_system_errno sqlite3_api->system_errno
|
||||
/* Version 3.14.0 and later */
|
||||
#define sqlite3_trace_v2 sqlite3_api->trace_v2
|
||||
#define sqlite3_expanded_sql sqlite3_api->expanded_sql
|
||||
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
|
||||
|
||||
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
||||
|
@ -1241,6 +1241,15 @@ void sqlite3CryptFunc(sqlite3_context*,int,sqlite3_value**);
|
||||
const char*);
|
||||
#endif
|
||||
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
/* This is an extra SQLITE_TRACE macro that indicates "legacy" tracing
|
||||
** in the style of sqlite3_trace()
|
||||
*/
|
||||
#define SQLITE_TRACE_LEGACY 0x80
|
||||
#else
|
||||
#define SQLITE_TRACE_LEGACY 0
|
||||
#endif /* SQLITE_OMIT_DEPRECATED */
|
||||
|
||||
|
||||
/*
|
||||
** Each database connection is an instance of the following structure.
|
||||
@ -1270,6 +1279,7 @@ struct sqlite3 {
|
||||
u8 suppressErr; /* Do not issue error messages if true */
|
||||
u8 vtabOnConflict; /* Value to return for s3_vtab_on_conflict() */
|
||||
u8 isTransactionSavepoint; /* True if the outermost savepoint is a TS */
|
||||
u8 mTrace; /* zero or more SQLITE_TRACE flags */
|
||||
int nextPagesize; /* Pagesize after VACUUM if >0 */
|
||||
u32 magic; /* Magic number for detect library misuse */
|
||||
int nChange; /* Value returned by sqlite3_changes() */
|
||||
@ -1290,7 +1300,7 @@ struct sqlite3 {
|
||||
int nVDestroy; /* Number of active OP_VDestroy operations */
|
||||
int nExtension; /* Number of loaded extensions */
|
||||
void **aExtension; /* Array of shared library handles */
|
||||
void (*xTrace)(void*,const char*); /* Trace function */
|
||||
int (*xTrace)(u32,void*,void*,void*); /* Trace function */
|
||||
void *pTraceArg; /* Argument to the trace function */
|
||||
void (*xProfile)(void*,const char*,u64); /* Profiling function */
|
||||
void *pProfileArg; /* Argument to profile function */
|
||||
|
338
src/tclsqlite.c
338
src/tclsqlite.c
@ -133,6 +133,7 @@ struct SqliteDb {
|
||||
char *zBusy; /* The busy callback routine */
|
||||
char *zCommit; /* The commit hook callback routine */
|
||||
char *zTrace; /* The trace callback routine */
|
||||
char *zTraceV2; /* The trace_v2 callback routine */
|
||||
char *zProfile; /* The profile callback routine */
|
||||
char *zProgress; /* The progress callback routine */
|
||||
char *zAuth; /* The authorization callback routine */
|
||||
@ -192,7 +193,7 @@ static void closeIncrblobChannels(SqliteDb *pDb){
|
||||
for(p=pDb->pIncrblob; p; p=pNext){
|
||||
pNext = p->pNext;
|
||||
|
||||
/* Note: Calling unregister here call Tcl_Close on the incrblob channel,
|
||||
/* Note: Calling unregister here call Tcl_Close on the incrblob channel,
|
||||
** which deletes the IncrblobChannel structure at *p. So do not
|
||||
** call Tcl_Free() here.
|
||||
*/
|
||||
@ -233,8 +234,8 @@ static int incrblobClose(ClientData instanceData, Tcl_Interp *interp){
|
||||
** Read data from an incremental blob channel.
|
||||
*/
|
||||
static int incrblobInput(
|
||||
ClientData instanceData,
|
||||
char *buf,
|
||||
ClientData instanceData,
|
||||
char *buf,
|
||||
int bufSize,
|
||||
int *errorCodePtr
|
||||
){
|
||||
@ -265,8 +266,8 @@ static int incrblobInput(
|
||||
** Write data to an incremental blob channel.
|
||||
*/
|
||||
static int incrblobOutput(
|
||||
ClientData instanceData,
|
||||
CONST char *buf,
|
||||
ClientData instanceData,
|
||||
CONST char *buf,
|
||||
int toWrite,
|
||||
int *errorCodePtr
|
||||
){
|
||||
@ -298,7 +299,7 @@ static int incrblobOutput(
|
||||
** Seek an incremental blob channel.
|
||||
*/
|
||||
static int incrblobSeek(
|
||||
ClientData instanceData,
|
||||
ClientData instanceData,
|
||||
long offset,
|
||||
int seekMode,
|
||||
int *errorCodePtr
|
||||
@ -323,8 +324,8 @@ static int incrblobSeek(
|
||||
}
|
||||
|
||||
|
||||
static void incrblobWatch(ClientData instanceData, int mode){
|
||||
/* NO-OP */
|
||||
static void incrblobWatch(ClientData instanceData, int mode){
|
||||
/* NO-OP */
|
||||
}
|
||||
static int incrblobHandle(ClientData instanceData, int dir, ClientData *hPtr){
|
||||
return TCL_ERROR;
|
||||
@ -352,11 +353,11 @@ static Tcl_ChannelType IncrblobChannelType = {
|
||||
** Create a new incrblob channel.
|
||||
*/
|
||||
static int createIncrblobChannel(
|
||||
Tcl_Interp *interp,
|
||||
SqliteDb *pDb,
|
||||
Tcl_Interp *interp,
|
||||
SqliteDb *pDb,
|
||||
const char *zDb,
|
||||
const char *zTable,
|
||||
const char *zColumn,
|
||||
const char *zTable,
|
||||
const char *zColumn,
|
||||
sqlite_int64 iRow,
|
||||
int isReadonly
|
||||
){
|
||||
@ -438,7 +439,7 @@ static SqlFunc *findSqlFunc(SqliteDb *pDb, const char *zName){
|
||||
pNew = (SqlFunc*)Tcl_Alloc( sizeof(*pNew) + nName + 1 );
|
||||
pNew->zName = (char*)&pNew[1];
|
||||
memcpy(pNew->zName, zName, nName+1);
|
||||
for(p=pDb->pFunc; p; p=p->pNext){
|
||||
for(p=pDb->pFunc; p; p=p->pNext){
|
||||
if( sqlite3_stricmp(p->zName, pNew->zName)==0 ){
|
||||
Tcl_Free((char*)pNew);
|
||||
return p;
|
||||
@ -508,6 +509,9 @@ static void DbDeleteCmd(void *db){
|
||||
if( pDb->zTrace ){
|
||||
Tcl_Free(pDb->zTrace);
|
||||
}
|
||||
if( pDb->zTraceV2 ){
|
||||
Tcl_Free(pDb->zTraceV2);
|
||||
}
|
||||
if( pDb->zProfile ){
|
||||
Tcl_Free(pDb->zProfile);
|
||||
}
|
||||
@ -587,6 +591,82 @@ static void DbTraceHandler(void *cd, const char *zSql){
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef SQLITE_OMIT_TRACE
|
||||
/*
|
||||
** This routine is called by the SQLite trace_v2 handler whenever a new
|
||||
** supported event is generated. Unsupported event types are ignored.
|
||||
** The TCL script in pDb->zTraceV2 is executed, with the arguments for
|
||||
** the event appended to it (as list elements).
|
||||
*/
|
||||
static int DbTraceV2Handler(
|
||||
unsigned type, /* One of the SQLITE_TRACE_* event types. */
|
||||
void *cd, /* The original context data pointer. */
|
||||
void *pd, /* Primary event data, depends on event type. */
|
||||
void *xd /* Extra event data, depends on event type. */
|
||||
){
|
||||
SqliteDb *pDb = (SqliteDb*)cd;
|
||||
Tcl_Obj *pCmd;
|
||||
|
||||
switch( type ){
|
||||
case SQLITE_TRACE_STMT: {
|
||||
sqlite3_stmt *pStmt = (sqlite3_stmt *)pd;
|
||||
char *zSql = (char *)xd;
|
||||
|
||||
pCmd = Tcl_NewStringObj(pDb->zTraceV2, -1);
|
||||
Tcl_IncrRefCount(pCmd);
|
||||
Tcl_ListObjAppendElement(pDb->interp, pCmd,
|
||||
Tcl_NewWideIntObj((Tcl_WideInt)pStmt));
|
||||
Tcl_ListObjAppendElement(pDb->interp, pCmd,
|
||||
Tcl_NewStringObj(zSql, -1));
|
||||
Tcl_EvalObjEx(pDb->interp, pCmd, TCL_EVAL_DIRECT);
|
||||
Tcl_DecrRefCount(pCmd);
|
||||
Tcl_ResetResult(pDb->interp);
|
||||
break;
|
||||
}
|
||||
case SQLITE_TRACE_PROFILE: {
|
||||
sqlite3_stmt *pStmt = (sqlite3_stmt *)pd;
|
||||
sqlite3_int64 ns = (sqlite3_int64)xd;
|
||||
|
||||
pCmd = Tcl_NewStringObj(pDb->zTraceV2, -1);
|
||||
Tcl_IncrRefCount(pCmd);
|
||||
Tcl_ListObjAppendElement(pDb->interp, pCmd,
|
||||
Tcl_NewWideIntObj((Tcl_WideInt)pStmt));
|
||||
Tcl_ListObjAppendElement(pDb->interp, pCmd,
|
||||
Tcl_NewWideIntObj((Tcl_WideInt)ns));
|
||||
Tcl_EvalObjEx(pDb->interp, pCmd, TCL_EVAL_DIRECT);
|
||||
Tcl_DecrRefCount(pCmd);
|
||||
Tcl_ResetResult(pDb->interp);
|
||||
break;
|
||||
}
|
||||
case SQLITE_TRACE_ROW: {
|
||||
sqlite3_stmt *pStmt = (sqlite3_stmt *)pd;
|
||||
|
||||
pCmd = Tcl_NewStringObj(pDb->zTraceV2, -1);
|
||||
Tcl_IncrRefCount(pCmd);
|
||||
Tcl_ListObjAppendElement(pDb->interp, pCmd,
|
||||
Tcl_NewWideIntObj((Tcl_WideInt)pStmt));
|
||||
Tcl_EvalObjEx(pDb->interp, pCmd, TCL_EVAL_DIRECT);
|
||||
Tcl_DecrRefCount(pCmd);
|
||||
Tcl_ResetResult(pDb->interp);
|
||||
break;
|
||||
}
|
||||
case SQLITE_TRACE_CLOSE: {
|
||||
sqlite3 *db = (sqlite3 *)pd;
|
||||
|
||||
pCmd = Tcl_NewStringObj(pDb->zTraceV2, -1);
|
||||
Tcl_IncrRefCount(pCmd);
|
||||
Tcl_ListObjAppendElement(pDb->interp, pCmd,
|
||||
Tcl_NewWideIntObj((Tcl_WideInt)db));
|
||||
Tcl_EvalObjEx(pDb->interp, pCmd, TCL_EVAL_DIRECT);
|
||||
Tcl_DecrRefCount(pCmd);
|
||||
Tcl_ResetResult(pDb->interp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef SQLITE_OMIT_TRACE
|
||||
/*
|
||||
** This routine is called by the SQLite profile handler after a statement
|
||||
@ -637,9 +717,9 @@ static void DbRollbackHandler(void *clientData){
|
||||
** This procedure handles wal_hook callbacks.
|
||||
*/
|
||||
static int DbWalHandler(
|
||||
void *clientData,
|
||||
sqlite3 *db,
|
||||
const char *zDb,
|
||||
void *clientData,
|
||||
sqlite3 *db,
|
||||
const char *zDb,
|
||||
int nEntry
|
||||
){
|
||||
int ret = SQLITE_OK;
|
||||
@ -653,7 +733,7 @@ static int DbWalHandler(
|
||||
Tcl_IncrRefCount(p);
|
||||
Tcl_ListObjAppendElement(interp, p, Tcl_NewStringObj(zDb, -1));
|
||||
Tcl_ListObjAppendElement(interp, p, Tcl_NewIntObj(nEntry));
|
||||
if( TCL_OK!=Tcl_EvalObjEx(interp, p, 0)
|
||||
if( TCL_OK!=Tcl_EvalObjEx(interp, p, 0)
|
||||
|| TCL_OK!=Tcl_GetIntFromObj(interp, Tcl_GetObjResult(interp), &ret)
|
||||
){
|
||||
Tcl_BackgroundError(interp);
|
||||
@ -695,11 +775,11 @@ static void DbUnlockNotify(void **apArg, int nArg){
|
||||
** Pre-update hook callback.
|
||||
*/
|
||||
static void DbPreUpdateHandler(
|
||||
void *p,
|
||||
void *p,
|
||||
sqlite3 *db,
|
||||
int op,
|
||||
const char *zDb,
|
||||
const char *zTbl,
|
||||
const char *zDb,
|
||||
const char *zTbl,
|
||||
sqlite_int64 iKey1,
|
||||
sqlite_int64 iKey2
|
||||
){
|
||||
@ -727,10 +807,10 @@ static void DbPreUpdateHandler(
|
||||
#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
|
||||
|
||||
static void DbUpdateHandler(
|
||||
void *p,
|
||||
void *p,
|
||||
int op,
|
||||
const char *zDb,
|
||||
const char *zTbl,
|
||||
const char *zDb,
|
||||
const char *zTbl,
|
||||
sqlite_int64 rowid
|
||||
){
|
||||
SqliteDb *pDb = (SqliteDb *)p;
|
||||
@ -815,7 +895,7 @@ static void tclSqlFunc(sqlite3_context *context, int argc, sqlite3_value**argv){
|
||||
** script object, lappend the arguments, then evaluate the copy.
|
||||
**
|
||||
** By "shallow" copy, we mean only the outer list Tcl_Obj is duplicated.
|
||||
** The new Tcl_Obj contains pointers to the original list elements.
|
||||
** The new Tcl_Obj contains pointers to the original list elements.
|
||||
** That way, when Tcl_EvalObjv() is run and shimmers the first element
|
||||
** of the list to tclCmdNameType, that alternate representation will
|
||||
** be preserved and reused on the next invocation.
|
||||
@ -823,15 +903,15 @@ static void tclSqlFunc(sqlite3_context *context, int argc, sqlite3_value**argv){
|
||||
Tcl_Obj **aArg;
|
||||
int nArg;
|
||||
if( Tcl_ListObjGetElements(p->interp, p->pScript, &nArg, &aArg) ){
|
||||
sqlite3_result_error(context, Tcl_GetStringResult(p->interp), -1);
|
||||
sqlite3_result_error(context, Tcl_GetStringResult(p->interp), -1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
pCmd = Tcl_NewListObj(nArg, aArg);
|
||||
Tcl_IncrRefCount(pCmd);
|
||||
for(i=0; i<argc; i++){
|
||||
sqlite3_value *pIn = argv[i];
|
||||
Tcl_Obj *pVal;
|
||||
|
||||
|
||||
/* Set pVal to contain the i'th column of this row. */
|
||||
switch( sqlite3_value_type(pIn) ){
|
||||
case SQLITE_BLOB: {
|
||||
@ -866,7 +946,7 @@ static void tclSqlFunc(sqlite3_context *context, int argc, sqlite3_value**argv){
|
||||
rc = Tcl_ListObjAppendElement(p->interp, pCmd, pVal);
|
||||
if( rc ){
|
||||
Tcl_DecrRefCount(pCmd);
|
||||
sqlite3_result_error(context, Tcl_GetStringResult(p->interp), -1);
|
||||
sqlite3_result_error(context, Tcl_GetStringResult(p->interp), -1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -881,7 +961,7 @@ static void tclSqlFunc(sqlite3_context *context, int argc, sqlite3_value**argv){
|
||||
}
|
||||
|
||||
if( rc && rc!=TCL_RETURN ){
|
||||
sqlite3_result_error(context, Tcl_GetStringResult(p->interp), -1);
|
||||
sqlite3_result_error(context, Tcl_GetStringResult(p->interp), -1);
|
||||
}else{
|
||||
Tcl_Obj *pVar = Tcl_GetObjResult(p->interp);
|
||||
int n;
|
||||
@ -983,7 +1063,7 @@ static int auth_callback(
|
||||
Tcl_DStringAppendElement(&str, zArg4 ? zArg4 : "");
|
||||
#ifdef SQLITE_USER_AUTHENTICATION
|
||||
Tcl_DStringAppendElement(&str, zArg5 ? zArg5 : "");
|
||||
#endif
|
||||
#endif
|
||||
rc = Tcl_GlobalEval(pDb->interp, Tcl_DStringValue(&str));
|
||||
Tcl_DStringFree(&str);
|
||||
zReply = rc==TCL_OK ? Tcl_GetStringResult(pDb->interp) : "SQLITE_DENY";
|
||||
@ -1075,12 +1155,12 @@ static int DbTransPostCmd(
|
||||
pDb->disableAuth++;
|
||||
if( sqlite3_exec(pDb->db, zEnd, 0, 0, 0) ){
|
||||
/* This is a tricky scenario to handle. The most likely cause of an
|
||||
** error is that the exec() above was an attempt to commit the
|
||||
** error is that the exec() above was an attempt to commit the
|
||||
** top-level transaction that returned SQLITE_BUSY. Or, less likely,
|
||||
** that an IO-error has occurred. In either case, throw a Tcl exception
|
||||
** and try to rollback the transaction.
|
||||
**
|
||||
** But it could also be that the user executed one or more BEGIN,
|
||||
** But it could also be that the user executed one or more BEGIN,
|
||||
** COMMIT, SAVEPOINT, RELEASE or ROLLBACK commands that are confusing
|
||||
** this method's logic. Not clear how this would be best handled.
|
||||
*/
|
||||
@ -1099,7 +1179,7 @@ static int DbTransPostCmd(
|
||||
** Unless SQLITE_TEST is defined, this function is a simple wrapper around
|
||||
** sqlite3_prepare_v2(). If SQLITE_TEST is defined, then it uses either
|
||||
** sqlite3_prepare_v2() or legacy interface sqlite3_prepare(), depending
|
||||
** on whether or not the [db_use_legacy_prepare] command has been used to
|
||||
** on whether or not the [db_use_legacy_prepare] command has been used to
|
||||
** configure the connection.
|
||||
*/
|
||||
static int dbPrepare(
|
||||
@ -1155,7 +1235,7 @@ static int dbPrepareAndBind(
|
||||
|
||||
for(pPreStmt = pDb->stmtList; pPreStmt; pPreStmt=pPreStmt->pNext){
|
||||
int n = pPreStmt->nSql;
|
||||
if( nSql>=n
|
||||
if( nSql>=n
|
||||
&& memcmp(pPreStmt->zSql, zSql, n)==0
|
||||
&& (zSql[n]==0 || zSql[n-1]==';')
|
||||
){
|
||||
@ -1181,7 +1261,7 @@ static int dbPrepareAndBind(
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* If no prepared statement was found. Compile the SQL text. Also allocate
|
||||
** a new SqlPreparedStmt structure. */
|
||||
if( pPreStmt==0 ){
|
||||
@ -1227,7 +1307,7 @@ static int dbPrepareAndBind(
|
||||
assert( strlen30(pPreStmt->zSql)==pPreStmt->nSql );
|
||||
assert( 0==memcmp(pPreStmt->zSql, zSql, pPreStmt->nSql) );
|
||||
|
||||
/* Bind values to parameters that begin with $ or : */
|
||||
/* Bind values to parameters that begin with $ or : */
|
||||
for(i=1; i<=nVar; i++){
|
||||
const char *zVar = sqlite3_bind_parameter_name(pStmt, i);
|
||||
if( zVar!=0 && (zVar[0]=='$' || zVar[0]==':' || zVar[0]=='@') ){
|
||||
@ -1315,8 +1395,8 @@ static void dbReleaseStmt(
|
||||
assert( pDb->nStmt>0 );
|
||||
}
|
||||
pDb->nStmt++;
|
||||
|
||||
/* If we have too many statement in cache, remove the surplus from
|
||||
|
||||
/* If we have too many statement in cache, remove the surplus from
|
||||
** the end of the cache list. */
|
||||
while( pDb->nStmt>pDb->maxStmt ){
|
||||
SqlPreparedStmt *pLast = pDb->stmtLast;
|
||||
@ -1370,8 +1450,8 @@ static void dbReleaseColumnNames(DbEvalContext *p){
|
||||
** If pArray is not NULL, then it contains the name of a Tcl array
|
||||
** variable. The "*" member of this array is set to a list containing
|
||||
** the names of the columns returned by the statement as part of each
|
||||
** call to dbEvalStep(), in order from left to right. e.g. if the names
|
||||
** of the returned columns are a, b and c, it does the equivalent of the
|
||||
** call to dbEvalStep(), in order from left to right. e.g. if the names
|
||||
** of the returned columns are a, b and c, it does the equivalent of the
|
||||
** tcl command:
|
||||
**
|
||||
** set ${pArray}(*) {a b c}
|
||||
@ -1492,7 +1572,7 @@ static int dbEvalStep(DbEvalContext *p){
|
||||
#if SQLITE_TEST
|
||||
if( p->pDb->bLegacyPrepare && rcs==SQLITE_SCHEMA && zPrevSql ){
|
||||
/* If the runtime error was an SQLITE_SCHEMA, and the database
|
||||
** handle is configured to use the legacy sqlite3_prepare()
|
||||
** handle is configured to use the legacy sqlite3_prepare()
|
||||
** interface, retry prepare()/step() on the same SQL statement.
|
||||
** This only happens once. If there is a second SQLITE_SCHEMA
|
||||
** error, the error will be returned to the caller. */
|
||||
@ -1580,11 +1660,11 @@ static int DbUseNre(void){
|
||||
return( (major==8 && minor>=6) || major>8 );
|
||||
}
|
||||
#else
|
||||
/*
|
||||
/*
|
||||
** Compiling using headers earlier than 8.6. In this case NR cannot be
|
||||
** used, so DbUseNre() to always return zero. Add #defines for the other
|
||||
** Tcl_NRxxx() functions to prevent them from causing compilation errors,
|
||||
** even though the only invocations of them are within conditional blocks
|
||||
** even though the only invocations of them are within conditional blocks
|
||||
** of the form:
|
||||
**
|
||||
** if( DbUseNre() ) { ... }
|
||||
@ -1630,11 +1710,11 @@ static int DbEvalNextCmd(
|
||||
}
|
||||
}
|
||||
|
||||
/* The required interpreter variables are now populated with the data
|
||||
/* The required interpreter variables are now populated with the data
|
||||
** from the current row. If using NRE, schedule callbacks to evaluate
|
||||
** script pScript, then to invoke this function again to fetch the next
|
||||
** row (or clean up if there is no next row or the script throws an
|
||||
** exception). After scheduling the callbacks, return control to the
|
||||
** exception). After scheduling the callbacks, return control to the
|
||||
** caller.
|
||||
**
|
||||
** If not using NRE, evaluate pScript directly and continue with the
|
||||
@ -1659,7 +1739,7 @@ static int DbEvalNextCmd(
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is used by the implementations of the following database
|
||||
** This function is used by the implementations of the following database
|
||||
** handle sub-commands:
|
||||
**
|
||||
** $db update_hook ?SCRIPT?
|
||||
@ -1726,9 +1806,10 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
"preupdate", "profile", "progress",
|
||||
"rekey", "restore", "rollback_hook",
|
||||
"status", "timeout", "total_changes",
|
||||
"trace", "transaction", "unlock_notify",
|
||||
"update_hook", "version", "wal_hook",
|
||||
0
|
||||
"trace", "trace_v2", "transaction",
|
||||
"unlock_notify", "update_hook", "version",
|
||||
"wal_hook",
|
||||
0
|
||||
};
|
||||
enum DB_enum {
|
||||
DB_AUTHORIZER, DB_BACKUP, DB_BUSY,
|
||||
@ -1741,8 +1822,9 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
DB_PREUPDATE, DB_PROFILE, DB_PROGRESS,
|
||||
DB_REKEY, DB_RESTORE, DB_ROLLBACK_HOOK,
|
||||
DB_STATUS, DB_TIMEOUT, DB_TOTAL_CHANGES,
|
||||
DB_TRACE, DB_TRANSACTION, DB_UNLOCK_NOTIFY,
|
||||
DB_UPDATE_HOOK, DB_VERSION, DB_WAL_HOOK,
|
||||
DB_TRACE, DB_TRACE_V2, DB_TRANSACTION,
|
||||
DB_UNLOCK_NOTIFY, DB_UPDATE_HOOK, DB_VERSION,
|
||||
DB_WAL_HOOK,
|
||||
};
|
||||
/* don't leave trailing commas on DB_enum, it confuses the AIX xlc compiler */
|
||||
|
||||
@ -1928,7 +2010,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
return TCL_ERROR;
|
||||
}else{
|
||||
if( TCL_ERROR==Tcl_GetIntFromObj(interp, objv[3], &n) ){
|
||||
Tcl_AppendResult( interp, "cannot convert \"",
|
||||
Tcl_AppendResult( interp, "cannot convert \"",
|
||||
Tcl_GetStringFromObj(objv[3],0), "\" to integer", (char*)0);
|
||||
return TCL_ERROR;
|
||||
}else{
|
||||
@ -1942,7 +2024,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
}
|
||||
}
|
||||
}else{
|
||||
Tcl_AppendResult( interp, "bad option \"",
|
||||
Tcl_AppendResult( interp, "bad option \"",
|
||||
Tcl_GetStringFromObj(objv[2],0), "\": must be flush or size",
|
||||
(char*)0);
|
||||
return TCL_ERROR;
|
||||
@ -1953,7 +2035,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
/* $db changes
|
||||
**
|
||||
** Return the number of rows that were modified, inserted, or deleted by
|
||||
** the most recent INSERT, UPDATE or DELETE statement, not including
|
||||
** the most recent INSERT, UPDATE or DELETE statement, not including
|
||||
** any changes made by trigger programs.
|
||||
*/
|
||||
case DB_CHANGES: {
|
||||
@ -2000,7 +2082,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
pCollate->zScript = (char*)&pCollate[1];
|
||||
pDb->pCollate = pCollate;
|
||||
memcpy(pCollate->zScript, zScript, nScript+1);
|
||||
if( sqlite3_create_collation(pDb->db, zName, SQLITE_UTF8,
|
||||
if( sqlite3_create_collation(pDb->db, zName, SQLITE_UTF8,
|
||||
pCollate, tclSqlCollate) ){
|
||||
Tcl_SetResult(interp, (char *)sqlite3_errmsg(pDb->db), TCL_VOLATILE);
|
||||
return TCL_ERROR;
|
||||
@ -2126,7 +2208,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
const char *zSep;
|
||||
const char *zNull;
|
||||
if( objc<5 || objc>7 ){
|
||||
Tcl_WrongNumArgs(interp, 2, objv,
|
||||
Tcl_WrongNumArgs(interp, 2, objv,
|
||||
"CONFLICT-ALGORITHM TABLE FILENAME ?SEPARATOR? ?NULLINDICATOR?");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
@ -2155,7 +2237,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
strcmp(zConflict, "fail" ) != 0 &&
|
||||
strcmp(zConflict, "ignore" ) != 0 &&
|
||||
strcmp(zConflict, "replace" ) != 0 ) {
|
||||
Tcl_AppendResult(interp, "Error: \"", zConflict,
|
||||
Tcl_AppendResult(interp, "Error: \"", zConflict,
|
||||
"\", conflict-algorithm must be one of: rollback, "
|
||||
"abort, fail, ignore, or replace", (char*)0);
|
||||
return TCL_ERROR;
|
||||
@ -2244,7 +2326,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
for(i=0; i<nCol; i++){
|
||||
/* check for null data, if so, bind as null */
|
||||
if( (nNull>0 && strcmp(azCol[i], zNull)==0)
|
||||
|| strlen30(azCol[i])==0
|
||||
|| strlen30(azCol[i])==0
|
||||
){
|
||||
sqlite3_bind_null(pStmt, i+1);
|
||||
}else{
|
||||
@ -2323,7 +2405,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
** The onecolumn method is the equivalent of:
|
||||
** lindex [$db eval $sql] 0
|
||||
*/
|
||||
case DB_EXISTS:
|
||||
case DB_EXISTS:
|
||||
case DB_ONECOLUMN: {
|
||||
Tcl_Obj *pResult = 0;
|
||||
DbEvalContext sEval;
|
||||
@ -2351,7 +2433,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** $db eval $sql ?array? ?{ ...code... }?
|
||||
**
|
||||
@ -2397,7 +2479,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
}
|
||||
pScript = objv[objc-1];
|
||||
Tcl_IncrRefCount(pScript);
|
||||
|
||||
|
||||
p = (DbEvalContext *)Tcl_Alloc(sizeof(DbEvalContext));
|
||||
dbEvalInit(p, pDb, objv[2], pArray);
|
||||
|
||||
@ -2444,7 +2526,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
if( n>2 && strncmp(z, "-deterministic",n)==0 ){
|
||||
flags |= SQLITE_DETERMINISTIC;
|
||||
}else{
|
||||
Tcl_AppendResult(interp, "bad option \"", z,
|
||||
Tcl_AppendResult(interp, "bad option \"", z,
|
||||
"\": must be -argcount or -deterministic", 0
|
||||
);
|
||||
return TCL_ERROR;
|
||||
@ -2553,7 +2635,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
}
|
||||
|
||||
/*
|
||||
** $db last_insert_rowid
|
||||
** $db last_insert_rowid
|
||||
**
|
||||
** Return an integer which is the ROWID for the most recent insert.
|
||||
*/
|
||||
@ -2575,7 +2657,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
*/
|
||||
|
||||
/* $db progress ?N CALLBACK?
|
||||
**
|
||||
**
|
||||
** Invoke the given callback every N virtual machine opcodes while executing
|
||||
** queries.
|
||||
*/
|
||||
@ -2682,7 +2764,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
|
||||
/* $db restore ?DATABASE? FILENAME
|
||||
**
|
||||
** Open a database file named FILENAME. Transfer the content
|
||||
** Open a database file named FILENAME. Transfer the content
|
||||
** of FILENAME into the local database DATABASE (default: "main").
|
||||
*/
|
||||
case DB_RESTORE: {
|
||||
@ -2743,7 +2825,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
/*
|
||||
** $db status (step|sort|autoindex)
|
||||
**
|
||||
** Display SQLITE_STMTSTATUS_FULLSCAN_STEP or
|
||||
** Display SQLITE_STMTSTATUS_FULLSCAN_STEP or
|
||||
** SQLITE_STMTSTATUS_SORT for the most recent eval.
|
||||
*/
|
||||
case DB_STATUS: {
|
||||
@ -2761,15 +2843,15 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
}else if( strcmp(zOp, "autoindex")==0 ){
|
||||
v = pDb->nIndex;
|
||||
}else{
|
||||
Tcl_AppendResult(interp,
|
||||
"bad argument: should be autoindex, step, or sort",
|
||||
Tcl_AppendResult(interp,
|
||||
"bad argument: should be autoindex, step, or sort",
|
||||
(char*)0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(v));
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** $db timeout MILLESECONDS
|
||||
**
|
||||
@ -2785,11 +2867,11 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
sqlite3_busy_timeout(pDb->db, ms);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** $db total_changes
|
||||
**
|
||||
** Return the number of rows that were modified, inserted, or deleted
|
||||
** Return the number of rows that were modified, inserted, or deleted
|
||||
** since the database handle was created.
|
||||
*/
|
||||
case DB_TOTAL_CHANGES: {
|
||||
@ -2842,6 +2924,88 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
break;
|
||||
}
|
||||
|
||||
/* $db trace_v2 ?CALLBACK? ?MASK?
|
||||
**
|
||||
** Make arrangements to invoke the CALLBACK routine for each trace event
|
||||
** matching the mask that is generated. The parameters are appended to
|
||||
** CALLBACK before it is executed.
|
||||
*/
|
||||
case DB_TRACE_V2: {
|
||||
if( objc>4 ){
|
||||
Tcl_WrongNumArgs(interp, 2, objv, "?CALLBACK? ?MASK?");
|
||||
return TCL_ERROR;
|
||||
}else if( objc==2 ){
|
||||
if( pDb->zTraceV2 ){
|
||||
Tcl_AppendResult(interp, pDb->zTraceV2, (char*)0);
|
||||
}
|
||||
}else{
|
||||
char *zTraceV2;
|
||||
int len;
|
||||
Tcl_WideInt wMask = 0;
|
||||
if( objc==4 ){
|
||||
static const char *TTYPE_strs[] = {
|
||||
"statement", "profile", "row", "close", 0
|
||||
};
|
||||
enum TTYPE_enum {
|
||||
TTYPE_STMT, TTYPE_PROFILE, TTYPE_ROW, TTYPE_CLOSE
|
||||
};
|
||||
int i;
|
||||
if( TCL_OK!=Tcl_ListObjLength(interp, objv[3], &len) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
for(i=0; i<len; i++){
|
||||
Tcl_Obj *pObj;
|
||||
int ttype;
|
||||
if( TCL_OK!=Tcl_ListObjIndex(interp, objv[3], i, &pObj) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( Tcl_GetIndexFromObj(interp, pObj, TTYPE_strs, "trace type",
|
||||
0, &ttype)!=TCL_OK ){
|
||||
Tcl_WideInt wType;
|
||||
Tcl_Obj *pError = Tcl_DuplicateObj(Tcl_GetObjResult(interp));
|
||||
Tcl_IncrRefCount(pError);
|
||||
if( TCL_OK==Tcl_GetWideIntFromObj(interp, pObj, &wType) ){
|
||||
Tcl_DecrRefCount(pError);
|
||||
wMask |= wType;
|
||||
}else{
|
||||
Tcl_SetObjResult(interp, pError);
|
||||
Tcl_DecrRefCount(pError);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
}else{
|
||||
switch( (enum TTYPE_enum)ttype ){
|
||||
case TTYPE_STMT: wMask |= SQLITE_TRACE_STMT; break;
|
||||
case TTYPE_PROFILE: wMask |= SQLITE_TRACE_PROFILE; break;
|
||||
case TTYPE_ROW: wMask |= SQLITE_TRACE_ROW; break;
|
||||
case TTYPE_CLOSE: wMask |= SQLITE_TRACE_CLOSE; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}else{
|
||||
wMask = SQLITE_TRACE_STMT; /* use the "legacy" default */
|
||||
}
|
||||
if( pDb->zTraceV2 ){
|
||||
Tcl_Free(pDb->zTraceV2);
|
||||
}
|
||||
zTraceV2 = Tcl_GetStringFromObj(objv[2], &len);
|
||||
if( zTraceV2 && len>0 ){
|
||||
pDb->zTraceV2 = Tcl_Alloc( len + 1 );
|
||||
memcpy(pDb->zTraceV2, zTraceV2, len+1);
|
||||
}else{
|
||||
pDb->zTraceV2 = 0;
|
||||
}
|
||||
#if !defined(SQLITE_OMIT_TRACE) && !defined(SQLITE_OMIT_FLOATING_POINT)
|
||||
if( pDb->zTraceV2 ){
|
||||
pDb->interp = interp;
|
||||
sqlite3_trace_v2(pDb->db, (unsigned)wMask, DbTraceV2Handler, pDb);
|
||||
}else{
|
||||
sqlite3_trace_v2(pDb->db, 0, 0, 0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* $db transaction [-deferred|-immediate|-exclusive] SCRIPT
|
||||
**
|
||||
** Start a new transaction (if we are not already in the midst of a
|
||||
@ -2894,7 +3058,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
/* If using NRE, schedule a callback to invoke the script pScript, then
|
||||
** a second callback to commit (or rollback) the transaction or savepoint
|
||||
** opened above. If not using NRE, evaluate the script directly, then
|
||||
** call function DbTransPostCmd() to commit (or rollback) the transaction
|
||||
** call function DbTransPostCmd() to commit (or rollback) the transaction
|
||||
** or savepoint. */
|
||||
if( DbUseNre() ){
|
||||
Tcl_NRAddCallback(interp, DbTransPostCmd, cd, 0, 0, 0);
|
||||
@ -2925,14 +3089,14 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
Tcl_DecrRefCount(pDb->pUnlockNotify);
|
||||
pDb->pUnlockNotify = 0;
|
||||
}
|
||||
|
||||
|
||||
if( objc==3 ){
|
||||
xNotify = DbUnlockNotify;
|
||||
pNotifyArg = (void *)pDb;
|
||||
pDb->pUnlockNotify = objv[2];
|
||||
Tcl_IncrRefCount(pDb->pUnlockNotify);
|
||||
}
|
||||
|
||||
|
||||
if( sqlite3_unlock_notify(pDb->db, xNotify, pNotifyArg) ){
|
||||
Tcl_AppendResult(interp, sqlite3_errmsg(pDb->db), (char*)0);
|
||||
rc = TCL_ERROR;
|
||||
@ -3031,13 +3195,13 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
** $db update_hook ?script?
|
||||
** $db rollback_hook ?script?
|
||||
*/
|
||||
case DB_WAL_HOOK:
|
||||
case DB_UPDATE_HOOK:
|
||||
case DB_WAL_HOOK:
|
||||
case DB_UPDATE_HOOK:
|
||||
case DB_ROLLBACK_HOOK: {
|
||||
/* set ppHook to point at pUpdateHook or pRollbackHook, depending on
|
||||
/* set ppHook to point at pUpdateHook or pRollbackHook, depending on
|
||||
** whether [$db update_hook] or [$db rollback_hook] was invoked.
|
||||
*/
|
||||
Tcl_Obj **ppHook = 0;
|
||||
Tcl_Obj **ppHook = 0;
|
||||
if( choice==DB_WAL_HOOK ) ppHook = &pDb->pWalHook;
|
||||
if( choice==DB_UPDATE_HOOK ) ppHook = &pDb->pUpdateHook;
|
||||
if( choice==DB_ROLLBACK_HOOK ) ppHook = &pDb->pRollbackHook;
|
||||
@ -3198,7 +3362,7 @@ static int DbMain(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
}
|
||||
}
|
||||
if( objc<3 || (objc&1)!=1 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv,
|
||||
Tcl_WrongNumArgs(interp, 1, objv,
|
||||
"HANDLE FILENAME ?-vfs VFSNAME? ?-readonly BOOLEAN? ?-create BOOLEAN?"
|
||||
" ?-nomutex BOOLEAN? ?-fullmutex BOOLEAN? ?-uri BOOLEAN?"
|
||||
#if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_CODEC_FROM_TCL)
|
||||
@ -3490,7 +3654,7 @@ static void MD5Init(MD5Context *ctx){
|
||||
* Update context to reflect the concatenation of another buffer full
|
||||
* of bytes.
|
||||
*/
|
||||
static
|
||||
static
|
||||
void MD5Update(MD5Context *ctx, const unsigned char *buf, unsigned int len){
|
||||
uint32 t;
|
||||
|
||||
@ -3536,7 +3700,7 @@ void MD5Update(MD5Context *ctx, const unsigned char *buf, unsigned int len){
|
||||
}
|
||||
|
||||
/*
|
||||
* Final wrapup - pad to 64-byte boundary with the bit pattern
|
||||
* Final wrapup - pad to 64-byte boundary with the bit pattern
|
||||
* 1 0* (64-bit count of bits processed, MSB-first)
|
||||
*/
|
||||
static void MD5Final(unsigned char digest[16], MD5Context *ctx){
|
||||
@ -3612,7 +3776,7 @@ static void MD5DigestToBase10x8(unsigned char digest[16], char zDigest[50]){
|
||||
|
||||
/*
|
||||
** A TCL command for md5. The argument is the text to be hashed. The
|
||||
** Result is the hash in base64.
|
||||
** Result is the hash in base64.
|
||||
*/
|
||||
static int md5_cmd(void*cd, Tcl_Interp *interp, int argc, const char **argv){
|
||||
MD5Context ctx;
|
||||
@ -3621,7 +3785,7 @@ static int md5_cmd(void*cd, Tcl_Interp *interp, int argc, const char **argv){
|
||||
void (*converter)(unsigned char*, char*);
|
||||
|
||||
if( argc!=2 ){
|
||||
Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0],
|
||||
Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0],
|
||||
" TEXT\"", (char*)0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
@ -3646,13 +3810,13 @@ static int md5file_cmd(void*cd, Tcl_Interp*interp, int argc, const char **argv){
|
||||
char zBuf[10240];
|
||||
|
||||
if( argc!=2 ){
|
||||
Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0],
|
||||
Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0],
|
||||
" FILENAME\"", (char*)0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
in = fopen(argv[1],"rb");
|
||||
if( in==0 ){
|
||||
Tcl_AppendResult(interp,"unable to open file \"", argv[1],
|
||||
Tcl_AppendResult(interp,"unable to open file \"", argv[1],
|
||||
"\" for reading", (char*)0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
@ -3719,7 +3883,7 @@ static void md5finalize(sqlite3_context *context){
|
||||
sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
int Md5_Register(sqlite3 *db){
|
||||
int rc = sqlite3_create_function(db, "md5sum", -1, SQLITE_UTF8, 0, 0,
|
||||
int rc = sqlite3_create_function(db, "md5sum", -1, SQLITE_UTF8, 0, 0,
|
||||
md5step, md5finalize);
|
||||
sqlite3_overload_function(db, "md5sum", -1); /* To exercise this API */
|
||||
return rc;
|
||||
@ -3870,7 +4034,7 @@ static int db_last_stmt_ptr(
|
||||
** Configure the interpreter passed as the first argument to have access
|
||||
** to the commands and linked variables that make up:
|
||||
**
|
||||
** * the [sqlite3] extension itself,
|
||||
** * the [sqlite3] extension itself,
|
||||
**
|
||||
** * If SQLITE_TCLMD5 or SQLITE_TEST is defined, the Md5 commands, and
|
||||
**
|
||||
|
34
src/test1.c
34
src/test1.c
@ -3633,7 +3633,7 @@ static int test_bind_blob(
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
sqlite3_stmt *pStmt;
|
||||
int idx;
|
||||
int len, idx;
|
||||
int bytes;
|
||||
char *value;
|
||||
int rc;
|
||||
@ -3652,9 +3652,18 @@ static int test_bind_blob(
|
||||
|
||||
if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR;
|
||||
if( Tcl_GetIntFromObj(interp, objv[2], &idx) ) return TCL_ERROR;
|
||||
value = Tcl_GetString(objv[3]);
|
||||
|
||||
value = (char*)Tcl_GetByteArrayFromObj(objv[3], &len);
|
||||
if( Tcl_GetIntFromObj(interp, objv[4], &bytes) ) return TCL_ERROR;
|
||||
|
||||
if( bytes>len ){
|
||||
char zBuf[200];
|
||||
sqlite3_snprintf(sizeof(zBuf), zBuf,
|
||||
"cannot use %d blob bytes, have %d", bytes, len);
|
||||
Tcl_AppendResult(interp, zBuf, -1);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
rc = sqlite3_bind_blob(pStmt, idx, value, bytes, xDestructor);
|
||||
if( sqlite3TestErrCode(interp, StmtToDb(pStmt), rc) ) return TCL_ERROR;
|
||||
if( rc!=SQLITE_OK ){
|
||||
@ -4386,6 +4395,26 @@ static int test_sql(
|
||||
Tcl_SetResult(interp, (char *)sqlite3_sql(pStmt), TCL_VOLATILE);
|
||||
return TCL_OK;
|
||||
}
|
||||
static int test_ex_sql(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
sqlite3_stmt *pStmt;
|
||||
char *z;
|
||||
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "STMT");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR;
|
||||
z = sqlite3_expanded_sql(pStmt);
|
||||
Tcl_SetResult(interp, z, TCL_VOLATILE);
|
||||
sqlite3_free(z);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_column_count STMT
|
||||
@ -7276,6 +7305,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
{ "sqlite3_changes", test_changes ,0 },
|
||||
{ "sqlite3_step", test_step ,0 },
|
||||
{ "sqlite3_sql", test_sql ,0 },
|
||||
{ "sqlite3_expanded_sql", test_ex_sql ,0 },
|
||||
{ "sqlite3_next_stmt", test_next_stmt ,0 },
|
||||
{ "sqlite3_stmt_readonly", test_stmt_readonly ,0 },
|
||||
{ "sqlite3_stmt_busy", test_stmt_busy ,0 },
|
||||
|
@ -121,7 +121,7 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
|
||||
int saved_flags; /* Saved value of the db->flags */
|
||||
int saved_nChange; /* Saved value of db->nChange */
|
||||
int saved_nTotalChange; /* Saved value of db->nTotalChange */
|
||||
void (*saved_xTrace)(void*,const char*); /* Saved db->xTrace */
|
||||
u8 saved_mTrace; /* Saved trace settings */
|
||||
Db *pDb = 0; /* Database to detach at end of vacuum */
|
||||
int isMemDb; /* True if vacuuming a :memory: database */
|
||||
int nRes; /* Bytes of reserved space at the end of each page */
|
||||
@ -142,10 +142,10 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
|
||||
saved_flags = db->flags;
|
||||
saved_nChange = db->nChange;
|
||||
saved_nTotalChange = db->nTotalChange;
|
||||
saved_xTrace = db->xTrace;
|
||||
saved_mTrace = db->mTrace;
|
||||
db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks | SQLITE_PreferBuiltin;
|
||||
db->flags &= ~(SQLITE_ForeignKeys | SQLITE_ReverseOrder);
|
||||
db->xTrace = 0;
|
||||
db->mTrace = 0;
|
||||
|
||||
pMain = db->aDb[0].pBt;
|
||||
isMemDb = sqlite3PagerIsMemdb(sqlite3BtreePager(pMain));
|
||||
@ -345,7 +345,7 @@ end_of_vacuum:
|
||||
db->flags = saved_flags;
|
||||
db->nChange = saved_nChange;
|
||||
db->nTotalChange = saved_nTotalChange;
|
||||
db->xTrace = saved_xTrace;
|
||||
db->mTrace = saved_mTrace;
|
||||
sqlite3BtreeSetPageSize(pMain, -1, -1, 1);
|
||||
|
||||
/* Currently there is an SQL level transaction open on the vacuum
|
||||
|
18
src/vdbe.c
18
src/vdbe.c
@ -1383,6 +1383,10 @@ case OP_ResultRow: {
|
||||
}
|
||||
if( db->mallocFailed ) goto no_mem;
|
||||
|
||||
if( db->mTrace & SQLITE_TRACE_ROW ){
|
||||
db->xTrace(SQLITE_TRACE_ROW, db->pTraceArg, p, 0);
|
||||
}
|
||||
|
||||
/* Return SQLITE_ROW
|
||||
*/
|
||||
p->pc = (int)(pOp - aOp) + 1;
|
||||
@ -6781,13 +6785,21 @@ case OP_Init: { /* jump */
|
||||
char *z;
|
||||
|
||||
#ifndef SQLITE_OMIT_TRACE
|
||||
if( db->xTrace
|
||||
if( (db->mTrace & (SQLITE_TRACE_STMT|SQLITE_TRACE_LEGACY))!=0
|
||||
&& !p->doingRerun
|
||||
&& (zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql))!=0
|
||||
){
|
||||
z = sqlite3VdbeExpandSql(p, zTrace);
|
||||
db->xTrace(db->pTraceArg, z);
|
||||
sqlite3DbFree(db, z);
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
if( db->mTrace & SQLITE_TRACE_LEGACY ){
|
||||
void (*x)(void*,const char*) = (void(*)(void*,const char*))db->xTrace;
|
||||
x(db->pTraceArg, z);
|
||||
}else
|
||||
#endif
|
||||
{
|
||||
(void)db->xTrace(SQLITE_TRACE_STMT,db->pTraceArg,p,z);
|
||||
}
|
||||
sqlite3_free(z);
|
||||
}
|
||||
#ifdef SQLITE_USE_FCNTL_TRACE
|
||||
zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql);
|
||||
|
@ -60,12 +60,19 @@ static int vdbeSafetyNotNull(Vdbe *p){
|
||||
*/
|
||||
static SQLITE_NOINLINE void invokeProfileCallback(sqlite3 *db, Vdbe *p){
|
||||
sqlite3_int64 iNow;
|
||||
sqlite3_int64 iElapse;
|
||||
assert( p->startTime>0 );
|
||||
assert( db->xProfile!=0 );
|
||||
assert( db->xProfile!=0 || (db->mTrace & SQLITE_TRACE_PROFILE)!=0 );
|
||||
assert( db->init.busy==0 );
|
||||
assert( p->zSql!=0 );
|
||||
sqlite3OsCurrentTimeInt64(db->pVfs, &iNow);
|
||||
db->xProfile(db->pProfileArg, p->zSql, (iNow - p->startTime)*1000000);
|
||||
iElapse = (iNow - p->startTime)*1000000;
|
||||
if( db->xProfile ){
|
||||
db->xProfile(db->pProfileArg, p->zSql, iElapse);
|
||||
}
|
||||
if( db->mTrace & SQLITE_TRACE_PROFILE ){
|
||||
db->xTrace(SQLITE_TRACE_PROFILE, db->pTraceArg, p, (void*)&iElapse);
|
||||
}
|
||||
p->startTime = 0;
|
||||
}
|
||||
/*
|
||||
@ -569,7 +576,8 @@ static int sqlite3Step(Vdbe *p){
|
||||
);
|
||||
|
||||
#ifndef SQLITE_OMIT_TRACE
|
||||
if( db->xProfile && !db->init.busy && p->zSql ){
|
||||
if( (db->xProfile || (db->mTrace & SQLITE_TRACE_PROFILE)!=0)
|
||||
&& !db->init.busy && p->zSql ){
|
||||
sqlite3OsCurrentTimeInt64(db->pVfs, &p->startTime);
|
||||
}else{
|
||||
assert( p->startTime==0 );
|
||||
@ -1604,6 +1612,39 @@ int sqlite3_stmt_status(sqlite3_stmt *pStmt, int op, int resetFlag){
|
||||
return (int)v;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the SQL associated with a prepared statement
|
||||
*/
|
||||
const char *sqlite3_sql(sqlite3_stmt *pStmt){
|
||||
Vdbe *p = (Vdbe *)pStmt;
|
||||
return p ? p->zSql : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the SQL associated with a prepared statement with
|
||||
** bound parameters expanded. Space to hold the returned string is
|
||||
** obtained from sqlite3_malloc(). The caller is responsible for
|
||||
** freeing the returned string by passing it to sqlite3_free().
|
||||
**
|
||||
** The SQLITE_TRACE_SIZE_LIMIT puts an upper bound on the size of
|
||||
** expanded bound parameters.
|
||||
*/
|
||||
char *sqlite3_expanded_sql(sqlite3_stmt *pStmt){
|
||||
#ifdef SQLITE_OMIT_TRACE
|
||||
return 0;
|
||||
#else
|
||||
char *z = 0;
|
||||
const char *zSql = sqlite3_sql(pStmt);
|
||||
if( zSql ){
|
||||
Vdbe *p = (Vdbe *)pStmt;
|
||||
sqlite3_mutex_enter(p->db->mutex);
|
||||
z = sqlite3VdbeExpandSql(p, zSql);
|
||||
sqlite3_mutex_leave(p->db->mutex);
|
||||
}
|
||||
return z;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
|
||||
/*
|
||||
** Allocate and populate an UnpackedRecord structure based on the serialized
|
||||
|
@ -64,14 +64,6 @@ void sqlite3VdbeSetSql(Vdbe *p, const char *z, int n, int isPrepareV2){
|
||||
p->isPrepareV2 = (u8)isPrepareV2;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the SQL associated with a prepared statement
|
||||
*/
|
||||
const char *sqlite3_sql(sqlite3_stmt *pStmt){
|
||||
Vdbe *p = (Vdbe *)pStmt;
|
||||
return p ? p->zSql : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Swap all content between two VDBE structures.
|
||||
*/
|
||||
|
@ -81,10 +81,13 @@ char *sqlite3VdbeExpandSql(
|
||||
int i; /* Loop counter */
|
||||
Mem *pVar; /* Value of a host parameter */
|
||||
StrAccum out; /* Accumulate the output here */
|
||||
#ifndef SQLITE_OMIT_UTF16
|
||||
Mem utf8; /* Used to convert UTF16 parameters into UTF8 for display */
|
||||
#endif
|
||||
char zBase[100]; /* Initial working space */
|
||||
|
||||
db = p->db;
|
||||
sqlite3StrAccumInit(&out, db, zBase, sizeof(zBase),
|
||||
sqlite3StrAccumInit(&out, 0, zBase, sizeof(zBase),
|
||||
db->aLimit[SQLITE_LIMIT_LENGTH]);
|
||||
if( db->nVdbeExec>1 ){
|
||||
while( *zRawSql ){
|
||||
@ -135,12 +138,14 @@ char *sqlite3VdbeExpandSql(
|
||||
int nOut; /* Number of bytes of the string text to include in output */
|
||||
#ifndef SQLITE_OMIT_UTF16
|
||||
u8 enc = ENC(db);
|
||||
Mem utf8;
|
||||
if( enc!=SQLITE_UTF8 ){
|
||||
memset(&utf8, 0, sizeof(utf8));
|
||||
utf8.db = db;
|
||||
sqlite3VdbeMemSetStr(&utf8, pVar->z, pVar->n, enc, SQLITE_STATIC);
|
||||
sqlite3VdbeChangeEncoding(&utf8, SQLITE_UTF8);
|
||||
if( SQLITE_NOMEM==sqlite3VdbeChangeEncoding(&utf8, SQLITE_UTF8) ){
|
||||
out.accError = STRACCUM_NOMEM;
|
||||
out.nAlloc = 0;
|
||||
}
|
||||
pVar = &utf8;
|
||||
}
|
||||
#endif
|
||||
@ -182,6 +187,7 @@ char *sqlite3VdbeExpandSql(
|
||||
}
|
||||
}
|
||||
}
|
||||
if( out.accError ) sqlite3StrAccumReset(&out);
|
||||
return sqlite3StrAccumFinish(&out);
|
||||
}
|
||||
|
||||
|
@ -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, backup, busy, cache, changes, close, collate, collation_needed, commit_hook, complete, copy, enable_load_extension, errorcode, eval, exists, function, incrblob, interrupt, last_insert_rowid, nullvalue, onecolumn, preupdate, profile, progress, rekey, restore, rollback_hook, status, timeout, total_changes, trace, transaction, unlock_notify, update_hook, version, or wal_hook}}
|
||||
} {1 {bad option "bogus": must be authorizer, backup, busy, cache, changes, close, collate, collation_needed, commit_hook, complete, copy, enable_load_extension, errorcode, eval, exists, function, incrblob, interrupt, last_insert_rowid, nullvalue, onecolumn, preupdate, profile, progress, rekey, restore, rollback_hook, status, timeout, total_changes, trace, trace_v2, transaction, unlock_notify, update_hook, version, or wal_hook}}
|
||||
do_test tcl-1.2.1 {
|
||||
set v [catch {db cache bogus} msg]
|
||||
lappend v $msg
|
||||
|
233
test/trace3.test
Normal file
233
test/trace3.test
Normal file
@ -0,0 +1,233 @@
|
||||
# 2016 July 14
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
# This file implements regression tests for SQLite library. The focus of
|
||||
# this test file is the "sqlite3_trace_v2()" and "sqlite3_expanded_sql()"
|
||||
# APIs.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
ifcapable !trace { finish_test ; return }
|
||||
set ::testprefix trace3
|
||||
|
||||
proc trace_v2_error { args } {
|
||||
lappend ::stmtlist(error) [string trim $args]
|
||||
error "trace error"; # this will be ignored.
|
||||
}
|
||||
proc trace_v2_record { args } {
|
||||
lappend ::stmtlist(record) [string trim $args]
|
||||
}
|
||||
proc trace_v2_nop { args } {}; # do nothing.
|
||||
|
||||
do_test trace3-1.0 {
|
||||
execsql {
|
||||
CREATE TABLE t1(a,b);
|
||||
INSERT INTO t1 VALUES(1,NULL);
|
||||
INSERT INTO t1 VALUES(2,-1);
|
||||
INSERT INTO t1 VALUES(3,0);
|
||||
INSERT INTO t1 VALUES(4,1);
|
||||
INSERT INTO t1 VALUES(5,-2147483648);
|
||||
INSERT INTO t1 VALUES(6,2147483647);
|
||||
INSERT INTO t1 VALUES(7,-9223372036854775808);
|
||||
INSERT INTO t1 VALUES(8,9223372036854775807);
|
||||
INSERT INTO t1 VALUES(9,-1.0);
|
||||
INSERT INTO t1 VALUES(10,0.0);
|
||||
INSERT INTO t1 VALUES(11,1.0);
|
||||
INSERT INTO t1 VALUES(12,'');
|
||||
INSERT INTO t1 VALUES(13,'1');
|
||||
INSERT INTO t1 VALUES(14,'one');
|
||||
INSERT INTO t1 VALUES(15,x'abcd0123');
|
||||
INSERT INTO t1 VALUES(16,x'4567cdef');
|
||||
}
|
||||
} {}
|
||||
|
||||
do_test trace3-1.1 {
|
||||
set rc [catch {db trace_v2 1 2 3} msg]
|
||||
lappend rc $msg
|
||||
} {1 {wrong # args: should be "db trace_v2 ?CALLBACK? ?MASK?"}}
|
||||
do_test trace3-1.2 {
|
||||
set rc [catch {db trace_v2 1 bad} msg]
|
||||
lappend rc $msg
|
||||
} {1 {bad trace type "bad": must be statement, profile, row, or close}}
|
||||
|
||||
do_test trace3-2.1 {
|
||||
db trace_v2 trace_v2_nop
|
||||
db trace_v2
|
||||
} {trace_v2_nop}
|
||||
|
||||
do_test trace3-3.1 {
|
||||
unset -nocomplain ::stmtlist
|
||||
db trace_v2 trace_v2_nop
|
||||
execsql {
|
||||
SELECT a, b FROM t1 ORDER BY a;
|
||||
}
|
||||
array get ::stmtlist
|
||||
} {}
|
||||
do_test trace3-3.2 {
|
||||
set ::stmtlist(error) {}
|
||||
db trace_v2 trace_v2_error
|
||||
execsql {
|
||||
SELECT a, b FROM t1 ORDER BY a;
|
||||
}
|
||||
set ::stmtlist(error)
|
||||
} {/^\{-?\d+ \{SELECT a, b FROM t1 ORDER BY a;\}\}$/}
|
||||
do_test trace3-3.3 {
|
||||
set ::stmtlist(record) {}
|
||||
db trace_v2 trace_v2_record
|
||||
execsql {
|
||||
SELECT a, b FROM t1 ORDER BY a;
|
||||
}
|
||||
set ::stmtlist(record)
|
||||
} {/^\{-?\d+ \{SELECT a, b FROM t1 ORDER BY a;\}\}$/}
|
||||
do_test trace3-3.4 {
|
||||
set ::stmtlist(record) {}
|
||||
db trace_v2 trace_v2_record statement
|
||||
execsql {
|
||||
SELECT a, b FROM t1 ORDER BY a;
|
||||
}
|
||||
set ::stmtlist(record)
|
||||
} {/^\{-?\d+ \{SELECT a, b FROM t1 ORDER BY a;\}\}$/}
|
||||
do_test trace3-3.5 {
|
||||
set ::stmtlist(record) {}
|
||||
db trace_v2 trace_v2_record 1
|
||||
execsql {
|
||||
SELECT a, b FROM t1 ORDER BY a;
|
||||
}
|
||||
set ::stmtlist(record)
|
||||
} {/^\{-?\d+ \{SELECT a, b FROM t1 ORDER BY a;\}\}$/}
|
||||
|
||||
do_test trace3-4.1 {
|
||||
set ::stmtlist(record) {}
|
||||
db trace_v2 trace_v2_record profile
|
||||
execsql {
|
||||
SELECT a, b FROM t1 ORDER BY a;
|
||||
}
|
||||
set ::stmtlist(record)
|
||||
} {/^\{-?\d+ -?\d+\}$/}
|
||||
do_test trace3-4.2 {
|
||||
set ::stmtlist(record) {}
|
||||
db trace_v2 trace_v2_record 2
|
||||
execsql {
|
||||
SELECT a, b FROM t1 ORDER BY a;
|
||||
}
|
||||
set ::stmtlist(record)
|
||||
} {/^\{-?\d+ -?\d+\}$/}
|
||||
|
||||
do_test trace3-5.1 {
|
||||
set ::stmtlist(record) {}
|
||||
db trace_v2 trace_v2_record row
|
||||
execsql {
|
||||
SELECT a, b FROM t1 ORDER BY a;
|
||||
}
|
||||
set ::stmtlist(record)
|
||||
} "/^[string trim [string repeat {\d+ } 16]]\$/"
|
||||
do_test trace3-5.2 {
|
||||
set ::stmtlist(record) {}
|
||||
db trace_v2 trace_v2_record 4
|
||||
execsql {
|
||||
SELECT a, b FROM t1 ORDER BY a;
|
||||
}
|
||||
set ::stmtlist(record)
|
||||
} "/^[string trim [string repeat {\d+ } 16]]\$/"
|
||||
|
||||
do_test trace3-6.1 {
|
||||
set ::stmtlist(record) {}
|
||||
db trace_v2 trace_v2_record {profile row}
|
||||
execsql {
|
||||
SELECT a, b FROM t1 ORDER BY a;
|
||||
}
|
||||
set ::stmtlist(record)
|
||||
} "/^[string trim [string repeat {-?\d+ } 16]] \\\{-?\\d+ -?\\d+\\\}\$/"
|
||||
do_test trace3-6.2 {
|
||||
set ::stmtlist(record) {}
|
||||
db trace_v2 trace_v2_record {statement profile row}
|
||||
execsql {
|
||||
SELECT a, b FROM t1 ORDER BY a;
|
||||
}
|
||||
set ::stmtlist(record)
|
||||
} "/^\\\{-?\\d+ \\\{SELECT a, b FROM t1 ORDER BY a;\\\}\\\} [string trim \
|
||||
[string repeat {-?\d+ } 16]] \\\{-?\\d+ -?\\d+\\\}\$/"
|
||||
|
||||
do_test trace3-7.1 {
|
||||
set DB [sqlite3_connection_pointer db]
|
||||
|
||||
set STMT [sqlite3_prepare_v2 $DB \
|
||||
"SELECT a, b FROM t1 WHERE b = ? ORDER BY a;" -1 TAIL]
|
||||
} {/^[0-9A-Fa-f]+$/}
|
||||
|
||||
do_test trace3-8.1 {
|
||||
list [sqlite3_bind_null $STMT 1] [sqlite3_expanded_sql $STMT]
|
||||
} {{} {SELECT a, b FROM t1 WHERE b = NULL ORDER BY a;}}
|
||||
do_test trace3-8.2 {
|
||||
list [sqlite3_bind_int $STMT 1 123] [sqlite3_expanded_sql $STMT]
|
||||
} {{} {SELECT a, b FROM t1 WHERE b = 123 ORDER BY a;}}
|
||||
do_test trace3-8.3 {
|
||||
list [sqlite3_bind_int64 $STMT 1 123] [sqlite3_expanded_sql $STMT]
|
||||
} {{} {SELECT a, b FROM t1 WHERE b = 123 ORDER BY a;}}
|
||||
do_test trace3-8.4 {
|
||||
list [sqlite3_bind_text $STMT 1 "some string" 11] \
|
||||
[sqlite3_expanded_sql $STMT]
|
||||
} {{} {SELECT a, b FROM t1 WHERE b = 'some string' ORDER BY a;}}
|
||||
do_test trace3-8.5 {
|
||||
list [sqlite3_bind_text $STMT 1 "some 'bad' string" 17] \
|
||||
[sqlite3_expanded_sql $STMT]
|
||||
} {{} {SELECT a, b FROM t1 WHERE b = 'some ''bad'' string' ORDER BY a;}}
|
||||
do_test trace3-8.6 {
|
||||
list [sqlite3_bind_double $STMT 1 123] [sqlite3_expanded_sql $STMT]
|
||||
} {{} {SELECT a, b FROM t1 WHERE b = 123.0 ORDER BY a;}}
|
||||
do_test trace3-8.7 {
|
||||
list [sqlite3_bind_text16 $STMT 1 \
|
||||
[encoding convertto unicode hi\000yall\000] 16] \
|
||||
[sqlite3_expanded_sql $STMT]
|
||||
} {{} {SELECT a, b FROM t1 WHERE b = 'hi' ORDER BY a;}}
|
||||
do_test trace3-8.8 {
|
||||
list [sqlite3_bind_blob $STMT 1 "\x12\x34\x56" 3] \
|
||||
[sqlite3_expanded_sql $STMT]
|
||||
} {{} {SELECT a, b FROM t1 WHERE b = x'123456' ORDER BY a;}}
|
||||
do_test trace3-8.9 {
|
||||
list [sqlite3_bind_blob $STMT 1 "\xAB\xCD\xEF" 3] \
|
||||
[sqlite3_expanded_sql $STMT]
|
||||
} {{} {SELECT a, b FROM t1 WHERE b = x'abcdef' ORDER BY a;}}
|
||||
|
||||
do_test trace3-9.1 {
|
||||
sqlite3_finalize $STMT
|
||||
} {SQLITE_OK}
|
||||
|
||||
do_test trace3-10.1 {
|
||||
db trace_v2 ""
|
||||
db trace_v2
|
||||
} {}
|
||||
do_test trace3-10.2 {
|
||||
unset -nocomplain ::stmtlist
|
||||
db trace_v2 "" {statement profile row}
|
||||
execsql {
|
||||
SELECT a, b FROM t1 ORDER BY a;
|
||||
}
|
||||
array get ::stmtlist
|
||||
} {}
|
||||
|
||||
do_test trace3-11.1 {
|
||||
set ::stmtlist(record) {}
|
||||
db trace_v2 trace_v2_record close
|
||||
db close
|
||||
set ::stmtlist(record)
|
||||
} {/^-?\d+$/}
|
||||
|
||||
reset_db
|
||||
|
||||
do_test trace3-11.2 {
|
||||
set ::stmtlist(record) {}
|
||||
db trace_v2 trace_v2_record 8
|
||||
db close
|
||||
set ::stmtlist(record)
|
||||
} {/^-?\d+$/}
|
||||
|
||||
finish_test
|
Loading…
x
Reference in New Issue
Block a user