1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-27 20:41:58 +03:00

Initial work on the Tcl API interface to the new sqlite3_trace_v2() function.

FossilOrigin-Name: 7b59fa40a01c89cc98414d90a798169c26e04256
This commit is contained in:
mistachkin
2016-07-14 21:26:09 +00:00
parent 0129a54a16
commit b56660f5a4
3 changed files with 223 additions and 94 deletions

View File

@ -133,6 +133,7 @@ struct SqliteDb {
char *zBusy; /* The busy callback routine */
char *zCommit; /* The commit hook callback routine */
char *zTrace; /* The trace callback routine */
char *zTraceV2; /* The trace_v2 callback routine */
char *zProfile; /* The profile callback routine */
char *zProgress; /* The progress callback routine */
char *zAuth; /* The authorization callback routine */
@ -192,7 +193,7 @@ static void closeIncrblobChannels(SqliteDb *pDb){
for(p=pDb->pIncrblob; p; p=pNext){
pNext = p->pNext;
/* Note: Calling unregister here call Tcl_Close on the incrblob channel,
/* Note: Calling unregister here call Tcl_Close on the incrblob channel,
** which deletes the IncrblobChannel structure at *p. So do not
** call Tcl_Free() here.
*/
@ -233,8 +234,8 @@ static int incrblobClose(ClientData instanceData, Tcl_Interp *interp){
** Read data from an incremental blob channel.
*/
static int incrblobInput(
ClientData instanceData,
char *buf,
ClientData instanceData,
char *buf,
int bufSize,
int *errorCodePtr
){
@ -265,8 +266,8 @@ static int incrblobInput(
** Write data to an incremental blob channel.
*/
static int incrblobOutput(
ClientData instanceData,
CONST char *buf,
ClientData instanceData,
CONST char *buf,
int toWrite,
int *errorCodePtr
){
@ -298,7 +299,7 @@ static int incrblobOutput(
** Seek an incremental blob channel.
*/
static int incrblobSeek(
ClientData instanceData,
ClientData instanceData,
long offset,
int seekMode,
int *errorCodePtr
@ -323,8 +324,8 @@ static int incrblobSeek(
}
static void incrblobWatch(ClientData instanceData, int mode){
/* NO-OP */
static void incrblobWatch(ClientData instanceData, int mode){
/* NO-OP */
}
static int incrblobHandle(ClientData instanceData, int dir, ClientData *hPtr){
return TCL_ERROR;
@ -352,11 +353,11 @@ static Tcl_ChannelType IncrblobChannelType = {
** Create a new incrblob channel.
*/
static int createIncrblobChannel(
Tcl_Interp *interp,
SqliteDb *pDb,
Tcl_Interp *interp,
SqliteDb *pDb,
const char *zDb,
const char *zTable,
const char *zColumn,
const char *zTable,
const char *zColumn,
sqlite_int64 iRow,
int isReadonly
){
@ -438,7 +439,7 @@ static SqlFunc *findSqlFunc(SqliteDb *pDb, const char *zName){
pNew = (SqlFunc*)Tcl_Alloc( sizeof(*pNew) + nName + 1 );
pNew->zName = (char*)&pNew[1];
memcpy(pNew->zName, zName, nName+1);
for(p=pDb->pFunc; p; p=p->pNext){
for(p=pDb->pFunc; p; p=p->pNext){
if( sqlite3_stricmp(p->zName, pNew->zName)==0 ){
Tcl_Free((char*)pNew);
return p;
@ -508,6 +509,9 @@ static void DbDeleteCmd(void *db){
if( pDb->zTrace ){
Tcl_Free(pDb->zTrace);
}
if( pDb->zTraceV2 ){
Tcl_Free(pDb->zTraceV2);
}
if( pDb->zProfile ){
Tcl_Free(pDb->zProfile);
}
@ -587,6 +591,82 @@ static void DbTraceHandler(void *cd, const char *zSql){
}
#endif
#ifndef SQLITE_OMIT_TRACE
/*
** This routine is called by the SQLite trace_v2 handler whenever a new
** supported event is generated. Unsupported event types are ignored.
** The TCL script in pDb->zTraceV2 is executed, with the arguments for
** the event appended to it (as list elements).
*/
static int DbTraceV2Handler(
unsigned type, /* One of the SQLITE_TRACE_* event types. */
void *cd, /* The original context data pointer. */
void *pd, /* Primary event data, depends on event type. */
void *xd /* Extra event data, depends on event type. */
){
SqliteDb *pDb = (SqliteDb*)cd;
Tcl_Obj *pCmd;
switch( type ){
case SQLITE_TRACE_STMT: {
sqlite3_stmt *pStmt = (sqlite3_stmt *)pd;
char *zSql = (char *)xd;
pCmd = Tcl_NewStringObj(pDb->zTraceV2, -1);
Tcl_IncrRefCount(pCmd);
Tcl_ListObjAppendElement(pDb->interp, pCmd,
Tcl_NewWideIntObj((Tcl_WideInt)pStmt));
Tcl_ListObjAppendElement(pDb->interp, pCmd,
Tcl_NewStringObj(zSql, -1));
Tcl_EvalObjEx(pDb->interp, pCmd, TCL_EVAL_DIRECT);
Tcl_DecrRefCount(pCmd);
Tcl_ResetResult(pDb->interp);
break;
}
case SQLITE_TRACE_PROFILE: {
sqlite3_stmt *pStmt = (sqlite3_stmt *)pd;
sqlite3_int64 ns = (sqlite3_int64)xd;
pCmd = Tcl_NewStringObj(pDb->zTraceV2, -1);
Tcl_IncrRefCount(pCmd);
Tcl_ListObjAppendElement(pDb->interp, pCmd,
Tcl_NewWideIntObj((Tcl_WideInt)pStmt));
Tcl_ListObjAppendElement(pDb->interp, pCmd,
Tcl_NewWideIntObj((Tcl_WideInt)ns));
Tcl_EvalObjEx(pDb->interp, pCmd, TCL_EVAL_DIRECT);
Tcl_DecrRefCount(pCmd);
Tcl_ResetResult(pDb->interp);
break;
}
case SQLITE_TRACE_ROW: {
sqlite3_stmt *pStmt = (sqlite3_stmt *)pd;
pCmd = Tcl_NewStringObj(pDb->zTraceV2, -1);
Tcl_IncrRefCount(pCmd);
Tcl_ListObjAppendElement(pDb->interp, pCmd,
Tcl_NewWideIntObj((Tcl_WideInt)pStmt));
Tcl_EvalObjEx(pDb->interp, pCmd, TCL_EVAL_DIRECT);
Tcl_DecrRefCount(pCmd);
Tcl_ResetResult(pDb->interp);
break;
}
case SQLITE_TRACE_CLOSE: {
sqlite3 *db = (sqlite3 *)pd;
pCmd = Tcl_NewStringObj(pDb->zTraceV2, -1);
Tcl_IncrRefCount(pCmd);
Tcl_ListObjAppendElement(pDb->interp, pCmd,
Tcl_NewWideIntObj((Tcl_WideInt)db));
Tcl_EvalObjEx(pDb->interp, pCmd, TCL_EVAL_DIRECT);
Tcl_DecrRefCount(pCmd);
Tcl_ResetResult(pDb->interp);
break;
}
}
return SQLITE_OK;
}
#endif
#ifndef SQLITE_OMIT_TRACE
/*
** This routine is called by the SQLite profile handler after a statement
@ -637,9 +717,9 @@ static void DbRollbackHandler(void *clientData){
** This procedure handles wal_hook callbacks.
*/
static int DbWalHandler(
void *clientData,
sqlite3 *db,
const char *zDb,
void *clientData,
sqlite3 *db,
const char *zDb,
int nEntry
){
int ret = SQLITE_OK;
@ -653,7 +733,7 @@ static int DbWalHandler(
Tcl_IncrRefCount(p);
Tcl_ListObjAppendElement(interp, p, Tcl_NewStringObj(zDb, -1));
Tcl_ListObjAppendElement(interp, p, Tcl_NewIntObj(nEntry));
if( TCL_OK!=Tcl_EvalObjEx(interp, p, 0)
if( TCL_OK!=Tcl_EvalObjEx(interp, p, 0)
|| TCL_OK!=Tcl_GetIntFromObj(interp, Tcl_GetObjResult(interp), &ret)
){
Tcl_BackgroundError(interp);
@ -695,11 +775,11 @@ static void DbUnlockNotify(void **apArg, int nArg){
** Pre-update hook callback.
*/
static void DbPreUpdateHandler(
void *p,
void *p,
sqlite3 *db,
int op,
const char *zDb,
const char *zTbl,
const char *zDb,
const char *zTbl,
sqlite_int64 iKey1,
sqlite_int64 iKey2
){
@ -727,10 +807,10 @@ static void DbPreUpdateHandler(
#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
static void DbUpdateHandler(
void *p,
void *p,
int op,
const char *zDb,
const char *zTbl,
const char *zDb,
const char *zTbl,
sqlite_int64 rowid
){
SqliteDb *pDb = (SqliteDb *)p;
@ -815,7 +895,7 @@ static void tclSqlFunc(sqlite3_context *context, int argc, sqlite3_value**argv){
** script object, lappend the arguments, then evaluate the copy.
**
** By "shallow" copy, we mean only the outer list Tcl_Obj is duplicated.
** The new Tcl_Obj contains pointers to the original list elements.
** The new Tcl_Obj contains pointers to the original list elements.
** That way, when Tcl_EvalObjv() is run and shimmers the first element
** of the list to tclCmdNameType, that alternate representation will
** be preserved and reused on the next invocation.
@ -823,15 +903,15 @@ static void tclSqlFunc(sqlite3_context *context, int argc, sqlite3_value**argv){
Tcl_Obj **aArg;
int nArg;
if( Tcl_ListObjGetElements(p->interp, p->pScript, &nArg, &aArg) ){
sqlite3_result_error(context, Tcl_GetStringResult(p->interp), -1);
sqlite3_result_error(context, Tcl_GetStringResult(p->interp), -1);
return;
}
}
pCmd = Tcl_NewListObj(nArg, aArg);
Tcl_IncrRefCount(pCmd);
for(i=0; i<argc; i++){
sqlite3_value *pIn = argv[i];
Tcl_Obj *pVal;
/* Set pVal to contain the i'th column of this row. */
switch( sqlite3_value_type(pIn) ){
case SQLITE_BLOB: {
@ -866,7 +946,7 @@ static void tclSqlFunc(sqlite3_context *context, int argc, sqlite3_value**argv){
rc = Tcl_ListObjAppendElement(p->interp, pCmd, pVal);
if( rc ){
Tcl_DecrRefCount(pCmd);
sqlite3_result_error(context, Tcl_GetStringResult(p->interp), -1);
sqlite3_result_error(context, Tcl_GetStringResult(p->interp), -1);
return;
}
}
@ -881,7 +961,7 @@ static void tclSqlFunc(sqlite3_context *context, int argc, sqlite3_value**argv){
}
if( rc && rc!=TCL_RETURN ){
sqlite3_result_error(context, Tcl_GetStringResult(p->interp), -1);
sqlite3_result_error(context, Tcl_GetStringResult(p->interp), -1);
}else{
Tcl_Obj *pVar = Tcl_GetObjResult(p->interp);
int n;
@ -983,7 +1063,7 @@ static int auth_callback(
Tcl_DStringAppendElement(&str, zArg4 ? zArg4 : "");
#ifdef SQLITE_USER_AUTHENTICATION
Tcl_DStringAppendElement(&str, zArg5 ? zArg5 : "");
#endif
#endif
rc = Tcl_GlobalEval(pDb->interp, Tcl_DStringValue(&str));
Tcl_DStringFree(&str);
zReply = rc==TCL_OK ? Tcl_GetStringResult(pDb->interp) : "SQLITE_DENY";
@ -1075,12 +1155,12 @@ static int DbTransPostCmd(
pDb->disableAuth++;
if( sqlite3_exec(pDb->db, zEnd, 0, 0, 0) ){
/* This is a tricky scenario to handle. The most likely cause of an
** error is that the exec() above was an attempt to commit the
** error is that the exec() above was an attempt to commit the
** top-level transaction that returned SQLITE_BUSY. Or, less likely,
** that an IO-error has occurred. In either case, throw a Tcl exception
** and try to rollback the transaction.
**
** But it could also be that the user executed one or more BEGIN,
** But it could also be that the user executed one or more BEGIN,
** COMMIT, SAVEPOINT, RELEASE or ROLLBACK commands that are confusing
** this method's logic. Not clear how this would be best handled.
*/
@ -1099,7 +1179,7 @@ static int DbTransPostCmd(
** Unless SQLITE_TEST is defined, this function is a simple wrapper around
** sqlite3_prepare_v2(). If SQLITE_TEST is defined, then it uses either
** sqlite3_prepare_v2() or legacy interface sqlite3_prepare(), depending
** on whether or not the [db_use_legacy_prepare] command has been used to
** on whether or not the [db_use_legacy_prepare] command has been used to
** configure the connection.
*/
static int dbPrepare(
@ -1155,7 +1235,7 @@ static int dbPrepareAndBind(
for(pPreStmt = pDb->stmtList; pPreStmt; pPreStmt=pPreStmt->pNext){
int n = pPreStmt->nSql;
if( nSql>=n
if( nSql>=n
&& memcmp(pPreStmt->zSql, zSql, n)==0
&& (zSql[n]==0 || zSql[n-1]==';')
){
@ -1181,7 +1261,7 @@ static int dbPrepareAndBind(
break;
}
}
/* If no prepared statement was found. Compile the SQL text. Also allocate
** a new SqlPreparedStmt structure. */
if( pPreStmt==0 ){
@ -1227,7 +1307,7 @@ static int dbPrepareAndBind(
assert( strlen30(pPreStmt->zSql)==pPreStmt->nSql );
assert( 0==memcmp(pPreStmt->zSql, zSql, pPreStmt->nSql) );
/* Bind values to parameters that begin with $ or : */
/* Bind values to parameters that begin with $ or : */
for(i=1; i<=nVar; i++){
const char *zVar = sqlite3_bind_parameter_name(pStmt, i);
if( zVar!=0 && (zVar[0]=='$' || zVar[0]==':' || zVar[0]=='@') ){
@ -1315,8 +1395,8 @@ static void dbReleaseStmt(
assert( pDb->nStmt>0 );
}
pDb->nStmt++;
/* If we have too many statement in cache, remove the surplus from
/* If we have too many statement in cache, remove the surplus from
** the end of the cache list. */
while( pDb->nStmt>pDb->maxStmt ){
SqlPreparedStmt *pLast = pDb->stmtLast;
@ -1370,8 +1450,8 @@ static void dbReleaseColumnNames(DbEvalContext *p){
** If pArray is not NULL, then it contains the name of a Tcl array
** variable. The "*" member of this array is set to a list containing
** the names of the columns returned by the statement as part of each
** call to dbEvalStep(), in order from left to right. e.g. if the names
** of the returned columns are a, b and c, it does the equivalent of the
** call to dbEvalStep(), in order from left to right. e.g. if the names
** of the returned columns are a, b and c, it does the equivalent of the
** tcl command:
**
** set ${pArray}(*) {a b c}
@ -1492,7 +1572,7 @@ static int dbEvalStep(DbEvalContext *p){
#if SQLITE_TEST
if( p->pDb->bLegacyPrepare && rcs==SQLITE_SCHEMA && zPrevSql ){
/* If the runtime error was an SQLITE_SCHEMA, and the database
** handle is configured to use the legacy sqlite3_prepare()
** handle is configured to use the legacy sqlite3_prepare()
** interface, retry prepare()/step() on the same SQL statement.
** This only happens once. If there is a second SQLITE_SCHEMA
** error, the error will be returned to the caller. */
@ -1580,11 +1660,11 @@ static int DbUseNre(void){
return( (major==8 && minor>=6) || major>8 );
}
#else
/*
/*
** Compiling using headers earlier than 8.6. In this case NR cannot be
** used, so DbUseNre() to always return zero. Add #defines for the other
** Tcl_NRxxx() functions to prevent them from causing compilation errors,
** even though the only invocations of them are within conditional blocks
** even though the only invocations of them are within conditional blocks
** of the form:
**
** if( DbUseNre() ) { ... }
@ -1630,11 +1710,11 @@ static int DbEvalNextCmd(
}
}
/* The required interpreter variables are now populated with the data
/* The required interpreter variables are now populated with the data
** from the current row. If using NRE, schedule callbacks to evaluate
** script pScript, then to invoke this function again to fetch the next
** row (or clean up if there is no next row or the script throws an
** exception). After scheduling the callbacks, return control to the
** exception). After scheduling the callbacks, return control to the
** caller.
**
** If not using NRE, evaluate pScript directly and continue with the
@ -1659,7 +1739,7 @@ static int DbEvalNextCmd(
}
/*
** This function is used by the implementations of the following database
** This function is used by the implementations of the following database
** handle sub-commands:
**
** $db update_hook ?SCRIPT?
@ -1726,9 +1806,10 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
"preupdate", "profile", "progress",
"rekey", "restore", "rollback_hook",
"status", "timeout", "total_changes",
"trace", "transaction", "unlock_notify",
"update_hook", "version", "wal_hook",
0
"trace", "trace_v2", "transaction",
"unlock_notify", "update_hook", "version",
"wal_hook",
0
};
enum DB_enum {
DB_AUTHORIZER, DB_BACKUP, DB_BUSY,
@ -1741,8 +1822,9 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
DB_PREUPDATE, DB_PROFILE, DB_PROGRESS,
DB_REKEY, DB_RESTORE, DB_ROLLBACK_HOOK,
DB_STATUS, DB_TIMEOUT, DB_TOTAL_CHANGES,
DB_TRACE, DB_TRANSACTION, DB_UNLOCK_NOTIFY,
DB_UPDATE_HOOK, DB_VERSION, DB_WAL_HOOK,
DB_TRACE, DB_TRACE_V2, DB_TRANSACTION,
DB_UNLOCK_NOTIFY, DB_UPDATE_HOOK, DB_VERSION,
DB_WAL_HOOK,
};
/* don't leave trailing commas on DB_enum, it confuses the AIX xlc compiler */
@ -1928,7 +2010,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
return TCL_ERROR;
}else{
if( TCL_ERROR==Tcl_GetIntFromObj(interp, objv[3], &n) ){
Tcl_AppendResult( interp, "cannot convert \"",
Tcl_AppendResult( interp, "cannot convert \"",
Tcl_GetStringFromObj(objv[3],0), "\" to integer", (char*)0);
return TCL_ERROR;
}else{
@ -1942,7 +2024,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
}
}
}else{
Tcl_AppendResult( interp, "bad option \"",
Tcl_AppendResult( interp, "bad option \"",
Tcl_GetStringFromObj(objv[2],0), "\": must be flush or size",
(char*)0);
return TCL_ERROR;
@ -1953,7 +2035,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
/* $db changes
**
** Return the number of rows that were modified, inserted, or deleted by
** the most recent INSERT, UPDATE or DELETE statement, not including
** the most recent INSERT, UPDATE or DELETE statement, not including
** any changes made by trigger programs.
*/
case DB_CHANGES: {
@ -2000,7 +2082,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
pCollate->zScript = (char*)&pCollate[1];
pDb->pCollate = pCollate;
memcpy(pCollate->zScript, zScript, nScript+1);
if( sqlite3_create_collation(pDb->db, zName, SQLITE_UTF8,
if( sqlite3_create_collation(pDb->db, zName, SQLITE_UTF8,
pCollate, tclSqlCollate) ){
Tcl_SetResult(interp, (char *)sqlite3_errmsg(pDb->db), TCL_VOLATILE);
return TCL_ERROR;
@ -2126,7 +2208,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
const char *zSep;
const char *zNull;
if( objc<5 || objc>7 ){
Tcl_WrongNumArgs(interp, 2, objv,
Tcl_WrongNumArgs(interp, 2, objv,
"CONFLICT-ALGORITHM TABLE FILENAME ?SEPARATOR? ?NULLINDICATOR?");
return TCL_ERROR;
}
@ -2155,7 +2237,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
strcmp(zConflict, "fail" ) != 0 &&
strcmp(zConflict, "ignore" ) != 0 &&
strcmp(zConflict, "replace" ) != 0 ) {
Tcl_AppendResult(interp, "Error: \"", zConflict,
Tcl_AppendResult(interp, "Error: \"", zConflict,
"\", conflict-algorithm must be one of: rollback, "
"abort, fail, ignore, or replace", (char*)0);
return TCL_ERROR;
@ -2244,7 +2326,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
for(i=0; i<nCol; i++){
/* check for null data, if so, bind as null */
if( (nNull>0 && strcmp(azCol[i], zNull)==0)
|| strlen30(azCol[i])==0
|| strlen30(azCol[i])==0
){
sqlite3_bind_null(pStmt, i+1);
}else{
@ -2323,7 +2405,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
** The onecolumn method is the equivalent of:
** lindex [$db eval $sql] 0
*/
case DB_EXISTS:
case DB_EXISTS:
case DB_ONECOLUMN: {
Tcl_Obj *pResult = 0;
DbEvalContext sEval;
@ -2351,7 +2433,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
}
break;
}
/*
** $db eval $sql ?array? ?{ ...code... }?
**
@ -2397,7 +2479,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
}
pScript = objv[objc-1];
Tcl_IncrRefCount(pScript);
p = (DbEvalContext *)Tcl_Alloc(sizeof(DbEvalContext));
dbEvalInit(p, pDb, objv[2], pArray);
@ -2444,7 +2526,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
if( n>2 && strncmp(z, "-deterministic",n)==0 ){
flags |= SQLITE_DETERMINISTIC;
}else{
Tcl_AppendResult(interp, "bad option \"", z,
Tcl_AppendResult(interp, "bad option \"", z,
"\": must be -argcount or -deterministic", 0
);
return TCL_ERROR;
@ -2553,7 +2635,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
}
/*
** $db last_insert_rowid
** $db last_insert_rowid
**
** Return an integer which is the ROWID for the most recent insert.
*/
@ -2575,7 +2657,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
*/
/* $db progress ?N CALLBACK?
**
**
** Invoke the given callback every N virtual machine opcodes while executing
** queries.
*/
@ -2682,7 +2764,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
/* $db restore ?DATABASE? FILENAME
**
** Open a database file named FILENAME. Transfer the content
** Open a database file named FILENAME. Transfer the content
** of FILENAME into the local database DATABASE (default: "main").
*/
case DB_RESTORE: {
@ -2743,7 +2825,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
/*
** $db status (step|sort|autoindex)
**
** Display SQLITE_STMTSTATUS_FULLSCAN_STEP or
** Display SQLITE_STMTSTATUS_FULLSCAN_STEP or
** SQLITE_STMTSTATUS_SORT for the most recent eval.
*/
case DB_STATUS: {
@ -2761,15 +2843,15 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
}else if( strcmp(zOp, "autoindex")==0 ){
v = pDb->nIndex;
}else{
Tcl_AppendResult(interp,
"bad argument: should be autoindex, step, or sort",
Tcl_AppendResult(interp,
"bad argument: should be autoindex, step, or sort",
(char*)0);
return TCL_ERROR;
}
Tcl_SetObjResult(interp, Tcl_NewIntObj(v));
break;
}
/*
** $db timeout MILLESECONDS
**
@ -2785,11 +2867,11 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
sqlite3_busy_timeout(pDb->db, ms);
break;
}
/*
** $db total_changes
**
** Return the number of rows that were modified, inserted, or deleted
** Return the number of rows that were modified, inserted, or deleted
** since the database handle was created.
*/
case DB_TOTAL_CHANGES: {
@ -2842,6 +2924,53 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
break;
}
/* $db trace_v2 ?CALLBACK? ?MASK?
**
** Make arrangements to invoke the CALLBACK routine for each trace event
** matching the mask that is generated. The parameters are appended to
** CALLBACK before it is executed.
*/
case DB_TRACE_V2: {
if( objc>4 ){
Tcl_WrongNumArgs(interp, 2, objv, "?CALLBACK? ?MASK?");
return TCL_ERROR;
}else if( objc==2 ){
if( pDb->zTraceV2 ){
Tcl_AppendResult(interp, pDb->zTraceV2, (char*)0);
}
}else{
Tcl_WideInt wMask;
char *zTraceV2;
int len;
if( objc==4 ){
if( TCL_OK!=Tcl_GetWideIntFromObj(interp, objv[3], &wMask) ){
return TCL_ERROR;
}
}else{
wMask = SQLITE_TRACE_STMT;
}
if( pDb->zTraceV2 ){
Tcl_Free(pDb->zTraceV2);
}
zTraceV2 = Tcl_GetStringFromObj(objv[2], &len);
if( zTraceV2 && len>0 ){
pDb->zTraceV2 = Tcl_Alloc( len + 1 );
memcpy(pDb->zTraceV2, zTraceV2, len+1);
}else{
pDb->zTraceV2 = 0;
}
#if !defined(SQLITE_OMIT_TRACE) && !defined(SQLITE_OMIT_FLOATING_POINT)
if( pDb->zTraceV2 ){
pDb->interp = interp;
sqlite3_trace_v2(pDb->db, (unsigned)wMask, DbTraceV2Handler, pDb);
}else{
sqlite3_trace_v2(pDb->db, 0, 0, 0);
}
#endif
}
break;
}
/* $db transaction [-deferred|-immediate|-exclusive] SCRIPT
**
** Start a new transaction (if we are not already in the midst of a
@ -2894,7 +3023,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
/* If using NRE, schedule a callback to invoke the script pScript, then
** a second callback to commit (or rollback) the transaction or savepoint
** opened above. If not using NRE, evaluate the script directly, then
** call function DbTransPostCmd() to commit (or rollback) the transaction
** call function DbTransPostCmd() to commit (or rollback) the transaction
** or savepoint. */
if( DbUseNre() ){
Tcl_NRAddCallback(interp, DbTransPostCmd, cd, 0, 0, 0);
@ -2925,14 +3054,14 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
Tcl_DecrRefCount(pDb->pUnlockNotify);
pDb->pUnlockNotify = 0;
}
if( objc==3 ){
xNotify = DbUnlockNotify;
pNotifyArg = (void *)pDb;
pDb->pUnlockNotify = objv[2];
Tcl_IncrRefCount(pDb->pUnlockNotify);
}
if( sqlite3_unlock_notify(pDb->db, xNotify, pNotifyArg) ){
Tcl_AppendResult(interp, sqlite3_errmsg(pDb->db), (char*)0);
rc = TCL_ERROR;
@ -3031,13 +3160,13 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
** $db update_hook ?script?
** $db rollback_hook ?script?
*/
case DB_WAL_HOOK:
case DB_UPDATE_HOOK:
case DB_WAL_HOOK:
case DB_UPDATE_HOOK:
case DB_ROLLBACK_HOOK: {
/* set ppHook to point at pUpdateHook or pRollbackHook, depending on
/* set ppHook to point at pUpdateHook or pRollbackHook, depending on
** whether [$db update_hook] or [$db rollback_hook] was invoked.
*/
Tcl_Obj **ppHook = 0;
Tcl_Obj **ppHook = 0;
if( choice==DB_WAL_HOOK ) ppHook = &pDb->pWalHook;
if( choice==DB_UPDATE_HOOK ) ppHook = &pDb->pUpdateHook;
if( choice==DB_ROLLBACK_HOOK ) ppHook = &pDb->pRollbackHook;
@ -3198,7 +3327,7 @@ static int DbMain(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
}
}
if( objc<3 || (objc&1)!=1 ){
Tcl_WrongNumArgs(interp, 1, objv,
Tcl_WrongNumArgs(interp, 1, objv,
"HANDLE FILENAME ?-vfs VFSNAME? ?-readonly BOOLEAN? ?-create BOOLEAN?"
" ?-nomutex BOOLEAN? ?-fullmutex BOOLEAN? ?-uri BOOLEAN?"
#if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_CODEC_FROM_TCL)
@ -3490,7 +3619,7 @@ static void MD5Init(MD5Context *ctx){
* Update context to reflect the concatenation of another buffer full
* of bytes.
*/
static
static
void MD5Update(MD5Context *ctx, const unsigned char *buf, unsigned int len){
uint32 t;
@ -3536,7 +3665,7 @@ void MD5Update(MD5Context *ctx, const unsigned char *buf, unsigned int len){
}
/*
* Final wrapup - pad to 64-byte boundary with the bit pattern
* Final wrapup - pad to 64-byte boundary with the bit pattern
* 1 0* (64-bit count of bits processed, MSB-first)
*/
static void MD5Final(unsigned char digest[16], MD5Context *ctx){
@ -3612,7 +3741,7 @@ static void MD5DigestToBase10x8(unsigned char digest[16], char zDigest[50]){
/*
** A TCL command for md5. The argument is the text to be hashed. The
** Result is the hash in base64.
** Result is the hash in base64.
*/
static int md5_cmd(void*cd, Tcl_Interp *interp, int argc, const char **argv){
MD5Context ctx;
@ -3621,7 +3750,7 @@ static int md5_cmd(void*cd, Tcl_Interp *interp, int argc, const char **argv){
void (*converter)(unsigned char*, char*);
if( argc!=2 ){
Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0],
Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0],
" TEXT\"", (char*)0);
return TCL_ERROR;
}
@ -3646,13 +3775,13 @@ static int md5file_cmd(void*cd, Tcl_Interp*interp, int argc, const char **argv){
char zBuf[10240];
if( argc!=2 ){
Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0],
Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0],
" FILENAME\"", (char*)0);
return TCL_ERROR;
}
in = fopen(argv[1],"rb");
if( in==0 ){
Tcl_AppendResult(interp,"unable to open file \"", argv[1],
Tcl_AppendResult(interp,"unable to open file \"", argv[1],
"\" for reading", (char*)0);
return TCL_ERROR;
}
@ -3719,7 +3848,7 @@ static void md5finalize(sqlite3_context *context){
sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT);
}
int Md5_Register(sqlite3 *db){
int rc = sqlite3_create_function(db, "md5sum", -1, SQLITE_UTF8, 0, 0,
int rc = sqlite3_create_function(db, "md5sum", -1, SQLITE_UTF8, 0, 0,
md5step, md5finalize);
sqlite3_overload_function(db, "md5sum", -1); /* To exercise this API */
return rc;
@ -3870,7 +3999,7 @@ static int db_last_stmt_ptr(
** Configure the interpreter passed as the first argument to have access
** to the commands and linked variables that make up:
**
** * the [sqlite3] extension itself,
** * the [sqlite3] extension itself,
**
** * If SQLITE_TCLMD5 or SQLITE_TEST is defined, the Md5 commands, and
**