Add the sqlite3_rollback_hook() API. Still requires further testing. (CVS 2823)
FossilOrigin-Name: 3baa3ff32435b64e7ae7646b17a98fef9296aaa0
diff --git a/src/btree.c b/src/btree.c
index ba5e6c4..65b5594 100644
--- a/src/btree.c
+++ b/src/btree.c
@@ -9,7 +9,7 @@
** May you share freely, never taking more than you give.
**
*************************************************************************
-** $Id: btree.c,v 1.273 2005/12/09 20:02:05 drh Exp $
+** $Id: btree.c,v 1.274 2005/12/16 06:54:02 danielk1977 Exp $
**
** This file implements a external (disk-based) database using BTrees.
** For a detailed discussion of BTrees, refer to
@@ -4906,9 +4906,11 @@
}
}
rc = clearDatabasePage(pBt, (Pgno)iTable, 0, 0);
+#if 0
if( rc ){
sqlite3BtreeRollback(pBt);
}
+#endif
return rc;
}
diff --git a/src/main.c b/src/main.c
index 8bc6bb8..ba0bb5f 100644
--- a/src/main.c
+++ b/src/main.c
@@ -14,7 +14,7 @@
** other files are for internal use by SQLite and should not be
** accessed by users of the library.
**
-** $Id: main.c,v 1.310 2005/12/15 15:22:09 danielk1977 Exp $
+** $Id: main.c,v 1.311 2005/12/16 06:54:02 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "os.h"
@@ -210,7 +210,14 @@
db->aDb[i].inTrans = 0;
}
}
- sqlite3ResetInternalSchema(db, 0);
+ if( db->flags&SQLITE_InternChanges ){
+ sqlite3ResetInternalSchema(db, 0);
+ }
+
+ /* If one has been configured, invoke the rollback-hook callback */
+ if( db->xRollbackCallback ){
+ db->xRollbackCallback(db->pRollbackArg);
+ }
}
/*
@@ -534,7 +541,7 @@
/*** EXPERIMENTAL ***
**
** Register a function to be invoked when a transaction comments.
-** If either function returns non-zero, then the commit becomes a
+** If the invoked function returns non-zero, then the commit becomes a
** rollback.
*/
void *sqlite3_commit_hook(
@@ -552,15 +559,31 @@
** Register a callback to be invoked each time a row is updated,
** inserted or deleted using this database connection.
*/
-void sqlite3_update_hook(
+void *sqlite3_update_hook(
sqlite3 *db, /* Attach the hook to this database */
void (*xCallback)(void*,int,char const *,char const *,sqlite_int64),
void *pArg /* Argument to the function */
){
+ void *pRet = db->pUpdateArg;
db->xUpdateCallback = xCallback;
db->pUpdateArg = pArg;
+ return pRet;
}
+/*
+** Register a callback to be invoked each time a transaction is rolled
+** back by this database connection.
+*/
+void *sqlite3_rollback_hook(
+ sqlite3 *db, /* Attach the hook to this database */
+ void (*xCallback)(void*), /* Callback function */
+ void *pArg /* Argument to the function */
+){
+ void *pRet = db->pRollbackArg;
+ db->xRollbackCallback = xCallback;
+ db->pRollbackArg = pArg;
+ return pRet;
+}
/*
** This routine is called to create a connection to a database BTree
diff --git a/src/sqlite.h.in b/src/sqlite.h.in
index 86d75d2..966b38a 100644
--- a/src/sqlite.h.in
+++ b/src/sqlite.h.in
@@ -12,7 +12,7 @@
** This header file defines the interface that the SQLite library
** presents to client programs.
**
-** @(#) $Id: sqlite.h.in,v 1.146 2005/12/15 15:22:09 danielk1977 Exp $
+** @(#) $Id: sqlite.h.in,v 1.147 2005/12/16 06:54:02 danielk1977 Exp $
*/
#ifndef _SQLITE3_H_
#define _SQLITE3_H_
@@ -1309,13 +1309,18 @@
**
** The update hook is not invoked when internal system tables are
** modified (i.e. sqlite_master and sqlite_sequence).
+**
+** If another function was previously registered, its pArg value is returned.
+** Otherwise NULL is returned.
*/
-void sqlite3_update_hook(
+void *sqlite3_update_hook(
sqlite3*,
void(*)(void *,int ,char const *,char const *,sqlite_int64),
void*
);
+void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*);
+
/*
** Undo the hack that converts floating point types to integer for
** builds on processors without floating point support.
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 0285677..11624ca 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -11,7 +11,7 @@
*************************************************************************
** Internal interface definitions for SQLite.
**
-** @(#) $Id: sqliteInt.h,v 1.438 2005/12/16 01:06:17 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.439 2005/12/16 06:54:02 danielk1977 Exp $
*/
#ifndef _SQLITEINT_H_
#define _SQLITEINT_H_
@@ -440,8 +440,10 @@
void *pTraceArg; /* Argument to the trace function */
void (*xProfile)(void*,const char*,u64); /* Profiling function */
void *pProfileArg; /* Argument to profile function */
- void *pCommitArg; /* Argument to xCommitCallback() */
- int (*xCommitCallback)(void*);/* Invoked at every commit. */
+ void *pCommitArg; /* Argument to xCommitCallback() */
+ int (*xCommitCallback)(void*); /* Invoked at every commit. */
+ void *pRollbackArg; /* Argument to xRollbackCallback() */
+ void (*xRollbackCallback)(void*); /* Invoked at every commit. */
void *pUpdateArg;
void (*xUpdateCallback)(void*,int, const char*,const char*,sqlite_int64);
void(*xCollNeeded)(void*,sqlite3*,int eTextRep,const char*);
diff --git a/src/tclsqlite.c b/src/tclsqlite.c
index b862df3..337cdef 100644
--- a/src/tclsqlite.c
+++ b/src/tclsqlite.c
@@ -11,7 +11,7 @@
*************************************************************************
** A TCL Interface to SQLite
**
-** $Id: tclsqlite.c,v 1.139 2005/12/15 15:22:10 danielk1977 Exp $
+** $Id: tclsqlite.c,v 1.140 2005/12/16 06:54:03 danielk1977 Exp $
*/
#ifndef NO_TCL /* Omit this whole file if TCL is unavailable */
@@ -100,6 +100,7 @@
char *zNull; /* Text to substitute for an SQL NULL value */
SqlFunc *pFunc; /* List of SQL functions */
Tcl_Obj *pUpdateHook; /* Update hook script (if any) */
+ Tcl_Obj *pRollbackHook; /* Rollback hook script (if any) */
SqlCollate *pCollate; /* List of SQL collation functions */
int rc; /* Return code of most recent sqlite3_exec() */
Tcl_Obj *pCollateNeeded; /* Collation needed script */
@@ -214,6 +215,9 @@
if( pDb->pUpdateHook ){
Tcl_DecrRefCount(pDb->pUpdateHook);
}
+ if( pDb->pRollbackHook ){
+ Tcl_DecrRefCount(pDb->pRollbackHook);
+ }
if( pDb->pCollateNeeded ){
Tcl_DecrRefCount(pDb->pCollateNeeded);
}
@@ -304,6 +308,14 @@
return 0;
}
+static void DbRollbackHandler(void *clientData){
+ SqliteDb *pDb = (SqliteDb*)clientData;
+ assert(pDb->pRollbackHook);
+ if( TCL_OK!=Tcl_EvalObjEx(pDb->interp, pDb->pRollbackHook, 0) ){
+ Tcl_BackgroundError(pDb->interp);
+ }
+}
+
static void DbUpdateHandler(
void *p,
int op,
@@ -653,10 +665,10 @@
"copy", "errorcode", "eval",
"exists", "function", "last_insert_rowid",
"nullvalue", "onecolumn", "profile",
- "progress", "rekey", "soft_heap_limit",
- "timeout", "total_changes", "trace",
- "transaction", "update_hook", "version",
- 0
+ "progress", "rekey", "rollback_hook",
+ "soft_heap_limit", "timeout", "total_changes",
+ "trace", "transaction", "update_hook",
+ "version", 0
};
enum DB_enum {
DB_AUTHORIZER, DB_BUSY, DB_CACHE,
@@ -665,9 +677,10 @@
DB_COPY, DB_ERRORCODE, DB_EVAL,
DB_EXISTS, DB_FUNCTION, DB_LAST_INSERT_ROWID,
DB_NULLVALUE, DB_ONECOLUMN, DB_PROFILE,
- DB_PROGRESS, DB_REKEY, DB_SOFT_HEAP_LIMIT,
- DB_TIMEOUT, DB_TOTAL_CHANGES, DB_TRACE,
- DB_TRANSACTION, DB_UPDATE_HOOK, DB_VERSION
+ DB_PROGRESS, DB_REKEY, DB_ROLLBACK_HOOK,
+ DB_SOFT_HEAP_LIMIT, DB_TIMEOUT, DB_TOTAL_CHANGES,
+ DB_TRACE, DB_TRANSACTION, DB_UPDATE_HOOK,
+ DB_VERSION
};
/* don't leave trailing commas on DB_enum, it confuses the AIX xlc compiler */
@@ -1872,28 +1885,43 @@
/*
** $db update_hook ?script?
+ ** $db rollback_hook ?script?
*/
- case DB_UPDATE_HOOK: {
+ case DB_UPDATE_HOOK:
+ case DB_ROLLBACK_HOOK: {
+
+ /* set ppHook to point at pUpdateHook or pRollbackHook, depending on
+ ** whether [$db update_hook] or [$db rollback_hook] was invoked.
+ */
+ Tcl_Obj **ppHook;
+ if( choice==DB_UPDATE_HOOK ){
+ ppHook = &pDb->pUpdateHook;
+ }else{
+ ppHook = &pDb->pRollbackHook;
+ }
+
if( objc!=2 && objc!=3 ){
Tcl_WrongNumArgs(interp, 2, objv, "?SCRIPT?");
return TCL_ERROR;
}
- if( pDb->pUpdateHook ){
- Tcl_SetObjResult(interp, pDb->pUpdateHook);
+ if( *ppHook ){
+ Tcl_SetObjResult(interp, *ppHook);
if( objc==3 ){
- Tcl_DecrRefCount(pDb->pUpdateHook);
- pDb->pUpdateHook = 0;
+ Tcl_DecrRefCount(*ppHook);
+ *ppHook = 0;
}
}
if( objc==3 ){
+ assert( !(*ppHook) );
if( Tcl_GetCharLength(objv[2])>0 ){
- pDb->pUpdateHook = objv[2];
- Tcl_IncrRefCount(pDb->pUpdateHook);
- sqlite3_update_hook(pDb->db, DbUpdateHandler, pDb);
- }else{
- sqlite3_update_hook(pDb->db, 0, 0);
+ *ppHook = objv[2];
+ Tcl_IncrRefCount(*ppHook);
}
}
+
+ sqlite3_update_hook(pDb->db, (pDb->pUpdateHook?DbUpdateHandler:0), pDb);
+ sqlite3_rollback_hook(pDb->db,(pDb->pRollbackHook?DbRollbackHandler:0),pDb);
+
break;
}
@@ -2160,7 +2188,7 @@
Sqlitetest4_Init(interp);
Sqlitetest5_Init(interp);
Sqlitetest6_Init(interp);
- Sqlitetestasync_Init(interp);
+ /* Sqlitetestasync_Init(interp); */
Md5_Init(interp);
#ifdef SQLITE_SSE
Sqlitetestsse_Init(interp);
diff --git a/src/vdbeaux.c b/src/vdbeaux.c
index 6949f8c..a99dc46 100644
--- a/src/vdbeaux.c
+++ b/src/vdbeaux.c
@@ -1233,17 +1233,27 @@
}
}
- /* If xFunc is not NULL, then it is one of sqlite3BtreeRollback,
+ /* If xFunc is not NULL, then it is one of
** sqlite3BtreeRollbackStmt or sqlite3BtreeCommitStmt. Call it once on
** each backend. If an error occurs and the return code is still
** SQLITE_OK, set the return code to the new error value.
*/
- for(i=0; xFunc && i<db->nDb; i++){
- int rc;
- Btree *pBt = db->aDb[i].pBt;
- if( pBt ){
- rc = xFunc(pBt);
- if( p->rc==SQLITE_OK ) p->rc = rc;
+ assert(!xFunc ||
+ xFunc==sqlite3BtreeCommitStmt ||
+ xFunc==sqlite3BtreeRollbackStmt ||
+ xFunc==sqlite3BtreeRollback
+ );
+ if( xFunc==sqlite3BtreeRollback ){
+ assert( p->rc!=SQLITE_OK );
+ sqlite3RollbackAll(db);
+ }else{
+ for(i=0; xFunc && i<db->nDb; i++){
+ int rc;
+ Btree *pBt = db->aDb[i].pBt;
+ if( pBt ){
+ rc = xFunc(pBt);
+ if( p->rc==SQLITE_OK ) p->rc = rc;
+ }
}
}