diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/main.c | 20 | ||||
-rw-r--r-- | src/sqlite.h.in | 19 | ||||
-rw-r--r-- | src/sqliteInt.h | 4 | ||||
-rw-r--r-- | src/tclsqlite.c | 73 | ||||
-rw-r--r-- | src/vdbe.c | 9 |
5 files changed, 112 insertions, 13 deletions
diff --git a/src/main.c b/src/main.c index 6386495e2..2b4a396ca 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.144 2003/12/06 21:43:56 drh Exp $ +** $Id: main.c,v 1.145 2004/01/15 02:44:03 drh Exp $ */ #include "sqliteInt.h" #include "os.h" @@ -976,6 +976,24 @@ void *sqlite_trace(sqlite *db, void (*xTrace)(void*,const char*), void *pArg){ return pOld; } +/*** EXPERIMENTAL *** +** +** Register a function to be invoked when a transaction comments. +** If either function returns non-zero, then the commit becomes a +** rollback. +*/ +void *sqlite_commit_hook( + sqlite *db, /* Attach the hook to this database */ + int (*xCallback)(void*), /* Function to invoke on each commit */ + void *pArg /* Argument to the function */ +){ + void *pOld = db->pCommitArg; + db->xCommitCallback = xCallback; + db->pCommitArg = pArg; + return pOld; +} + + /* ** This routine is called to create a connection to a database BTree ** driver. If zFilename is the name of a file, then that file is diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 6f8712145..e493a13fe 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.53 2003/10/18 09:37:26 danielk1977 Exp $ +** @(#) $Id: sqlite.h.in,v 1.54 2004/01/15 02:44:03 drh Exp $ */ #ifndef _SQLITE_H_ #define _SQLITE_H_ @@ -753,9 +753,26 @@ int sqlite_bind(sqlite_vm*, int idx, const char *value, int len, int copy); ** query is immediately terminated and any database changes rolled back. If the ** query was part of a larger transaction, then the transaction is not rolled ** back and remains active. The sqlite_exec() call returns SQLITE_ABORT. +** +******* THIS IS AN EXPERIMENTAL API AND IS SUBJECT TO CHANGE ****** */ void sqlite_progress_handler(sqlite*, int, int(*)(void*), void*); +/* +** Register a callback function to be invoked whenever a new transaction +** is committed. The pArg argument is passed through to the callback. +** callback. If the callback function returns non-zero, then the commit +** is converted into a rollback. +** +** If another function was previously registered, its pArg value is returned. +** Otherwise NULL is returned. +** +** Registering a NULL function disables the callback. +** +******* THIS IS AN EXPERIMENTAL API AND IS SUBJECT TO CHANGE ****** +*/ +void *sqlite_commit_hook(sqlite*, int(*)(void*), void*); + #ifdef __cplusplus } /* End of the 'extern "C"' block */ #endif diff --git a/src/sqliteInt.h b/src/sqliteInt.h index c90190af2..989478607 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.207 2004/01/07 03:04:27 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.208 2004/01/15 02:44:03 drh Exp $ */ #include "config.h" #include "sqlite.h" @@ -322,6 +322,8 @@ struct sqlite { int nTable; /* Number of tables in the database */ void *pBusyArg; /* 1st Argument to the busy callback */ int (*xBusyCallback)(void *,const char*,int); /* The busy callback */ + void *pCommitArg; /* Argument to xCommitCallback() */ + int (*xCommitCallback)(void*);/* Invoked at every commit. */ Hash aFunc; /* All functions that can be in SQL exprs */ int lastRowid; /* ROWID of most recent insert */ int priorNewRowid; /* Last randomly generated ROWID */ diff --git a/src/tclsqlite.c b/src/tclsqlite.c index c2f4cfd49..70706306a 100644 --- a/src/tclsqlite.c +++ b/src/tclsqlite.c @@ -11,7 +11,7 @@ ************************************************************************* ** A TCL Interface to SQLite ** -** $Id: tclsqlite.c,v 1.53 2003/12/19 12:32:46 drh Exp $ +** $Id: tclsqlite.c,v 1.54 2004/01/15 02:44:03 drh Exp $ */ #ifndef NO_TCL /* Omit this whole file if TCL is unavailable */ @@ -51,6 +51,7 @@ struct SqliteDb { sqlite *db; /* The "real" database structure */ Tcl_Interp *interp; /* The interpreter used for this database */ char *zBusy; /* The busy callback routine */ + char *zCommit; /* The commit hook callback routine */ char *zTrace; /* The trace callback routine */ char *zProgress; /* The progress callback routine */ char *zAuth; /* The authorization callback routine */ @@ -358,6 +359,23 @@ static void DbTraceHandler(void *cd, const char *zSql){ } /* +** This routine is called when a transaction is committed. The +** TCL script in pDb->zCommit is executed. If it returns non-zero or +** if it throws an exception, the transaction is rolled back instead +** of being committed. +*/ +static int DbCommitHandler(void *cd){ + SqliteDb *pDb = (SqliteDb*)cd; + int rc; + + rc = Tcl_Eval(pDb->interp, pDb->zCommit); + if( rc!=TCL_OK || atoi(Tcl_GetStringResult(pDb->interp)) ){ + return 1; + } + return 0; +} + +/* ** This routine is called to evaluate an SQL function implemented ** using TCL script. */ @@ -470,17 +488,17 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ int choice; static const char *DB_strs[] = { "authorizer", "busy", "changes", - "close", "complete", "errorcode", - "eval", "function", "last_insert_rowid", - "onecolumn", "timeout", "trace", - "progress", 0 + "close", "commit_hook", "complete", + "errorcode", "eval", "function", + "last_insert_rowid", "onecolumn", "progress", + "timeout", "trace", 0 }; enum DB_enum { DB_AUTHORIZER, DB_BUSY, DB_CHANGES, - DB_CLOSE, DB_COMPLETE, DB_ERRORCODE, - DB_EVAL, DB_FUNCTION, DB_LAST_INSERT_ROWID, - DB_ONECOLUMN, DB_TIMEOUT, DB_TRACE, - DB_PROGRESS + DB_CLOSE, DB_COMMIT_HOOK, DB_COMPLETE, + DB_ERRORCODE, DB_EVAL, DB_FUNCTION, + DB_LAST_INSERT_ROWID, DB_ONECOLUMN, DB_PROGRESS, + DB_TIMEOUT, DB_TRACE, }; if( objc<2 ){ @@ -649,6 +667,43 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ break; } + /* $db commit_hook ?CALLBACK? + ** + ** Invoke the given callback just before committing every SQL transaction. + ** If the callback throws an exception or returns non-zero, then the + ** transaction is aborted. If CALLBACK is an empty string, the callback + ** is disabled. + */ + case DB_COMMIT_HOOK: { + if( objc>3 ){ + Tcl_WrongNumArgs(interp, 2, objv, "?CALLBACK?"); + }else if( objc==2 ){ + if( pDb->zCommit ){ + Tcl_AppendResult(interp, pDb->zCommit, 0); + } + }else{ + char *zCommit; + int len; + if( pDb->zCommit ){ + Tcl_Free(pDb->zCommit); + } + zCommit = Tcl_GetStringFromObj(objv[2], &len); + if( zCommit && len>0 ){ + pDb->zCommit = Tcl_Alloc( len + 1 ); + strcpy(pDb->zCommit, zCommit); + }else{ + pDb->zCommit = 0; + } + if( pDb->zCommit ){ + pDb->interp = interp; + sqlite_commit_hook(pDb->db, DbCommitHandler, pDb); + }else{ + sqlite_commit_hook(pDb->db, 0, 0); + } + } + break; + } + /* $db complete SQL ** ** Return TRUE if SQL is a complete SQL statement. Return FALSE if diff --git a/src/vdbe.c b/src/vdbe.c index 505ff5fe3..64c8fe0fb 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -43,7 +43,7 @@ ** in this file for details. If in doubt, do not deviate from existing ** commenting and indentation practices when changing or adding code. ** -** $Id: vdbe.c,v 1.250 2004/01/14 21:59:23 drh Exp $ +** $Id: vdbe.c,v 1.251 2004/01/15 02:44:03 drh Exp $ */ #include "sqliteInt.h" #include "os.h" @@ -2245,6 +2245,13 @@ case OP_Transaction: { */ case OP_Commit: { int i; + if( db->xCommitCallback!=0 ){ + if( sqliteSafetyOff(db) ) goto abort_due_to_misuse; + if( db->xCommitCallback(db->pCommitArg)!=0 ){ + rc = SQLITE_CONSTRAINT; + } + if( sqliteSafetyOn(db) ) goto abort_due_to_misuse; + } for(i=0; rc==SQLITE_OK && i<db->nDb; i++){ if( db->aDb[i].inTrans ){ rc = sqliteBtreeCommit(db->aDb[i].pBt); |