Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Add the SQLITE_ENABLE_PREUPDATE_HOOK compile-time option. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | sessions |
Files: | files | file ages | folders |
SHA1: |
6634521461e6acff7cc778590e62d578 |
User & Date: | drh 2011-03-30 21:04:43.055 |
Context
2011-04-01
| ||
15:30 | If the sessions module is being built as part of the amalgamation, do not try to include sqliteInt.h and vdbeInt.h. (check-in: f87bfe6e12 user: dan tags: sessions) | |
2011-03-30
| ||
21:04 | Add the SQLITE_ENABLE_PREUPDATE_HOOK compile-time option. (check-in: 6634521461 user: drh tags: sessions) | |
17:25 | Disable the truncate optimization if there is a preupdate hook. (check-in: d051694e02 user: drh tags: sessions) | |
Changes
Changes to ext/session/session1.test.
︙ | ︙ | |||
12 13 14 15 16 17 18 19 20 21 22 23 24 25 | # if {![info exists testdir]} { set testdir [file join [file dirname [info script]] .. .. test] } source [file join [file dirname [info script]] session_common.tcl] source $testdir/tester.tcl set testprefix session1 proc do_changeset_test {tn session res} { set r [list] foreach x $res {lappend r $x} uplevel do_test $tn [list [subst -nocommands { | > | 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | # if {![info exists testdir]} { set testdir [file join [file dirname [info script]] .. .. test] } source [file join [file dirname [info script]] session_common.tcl] source $testdir/tester.tcl ifcapable !session {finish_test; return} set testprefix session1 proc do_changeset_test {tn session res} { set r [list] foreach x $res {lappend r $x} uplevel do_test $tn [list [subst -nocommands { |
︙ | ︙ | |||
440 441 442 443 444 445 446 | } do_db2_test 6.2 "SELECT * FROM t1" {a b 1 2} do_db2_test 6.3 "SELECT * FROM t2" {a b A B} catch { db2 close } finish_test | < | 441 442 443 444 445 446 447 | } do_db2_test 6.2 "SELECT * FROM t1" {a b 1 2} do_db2_test 6.3 "SELECT * FROM t2" {a b A B} catch { db2 close } finish_test |
Changes to ext/session/session2.test.
︙ | ︙ | |||
13 14 15 16 17 18 19 20 21 22 23 24 25 26 | # if {![info exists testdir]} { set testdir [file join [file dirname [info script]] .. .. test] } source [file join [file dirname [info script]] session_common.tcl] source $testdir/tester.tcl set testprefix session2 proc test_reset {} { catch { db close } catch { db2 close } forcedelete test.db test.db2 | > | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | # if {![info exists testdir]} { set testdir [file join [file dirname [info script]] .. .. test] } source [file join [file dirname [info script]] session_common.tcl] source $testdir/tester.tcl ifcapable !session {finish_test; return} set testprefix session2 proc test_reset {} { catch { db close } catch { db2 close } forcedelete test.db test.db2 |
︙ | ︙ |
Changes to ext/session/session3.test.
︙ | ︙ | |||
14 15 16 17 18 19 20 21 22 23 24 25 26 27 | # if {![info exists testdir]} { set testdir [file join [file dirname [info script]] .. .. test] } source [file join [file dirname [info script]] session_common.tcl] source $testdir/tester.tcl set testprefix session3 #------------------------------------------------------------------------- # These tests - session3-1.* - verify that the session module behaves # correctly when confronted with a schema mismatch when applying a # changeset (in function sqlite3changeset_apply()). | > | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | # if {![info exists testdir]} { set testdir [file join [file dirname [info script]] .. .. test] } source [file join [file dirname [info script]] session_common.tcl] source $testdir/tester.tcl ifcapable !session {finish_test; return} set testprefix session3 #------------------------------------------------------------------------- # These tests - session3-1.* - verify that the session module behaves # correctly when confronted with a schema mismatch when applying a # changeset (in function sqlite3changeset_apply()). |
︙ | ︙ | |||
173 174 175 176 177 178 179 | catch { db close } catch { db2 close } sqlite3_shutdown test_sqlite3_log sqlite3_initialize finish_test | < | 174 175 176 177 178 179 180 | catch { db close } catch { db2 close } sqlite3_shutdown test_sqlite3_log sqlite3_initialize finish_test |
Changes to ext/session/session4.test.
︙ | ︙ | |||
12 13 14 15 16 17 18 19 20 21 22 23 24 25 | # if {![info exists testdir]} { set testdir [file join [file dirname [info script]] .. .. test] } source [file join [file dirname [info script]] session_common.tcl] source $testdir/tester.tcl set testprefix session4 do_test 1.0 { execsql { CREATE TABLE x(a, b, c, d, e, PRIMARY KEY(c, e)); INSERT INTO x VALUES(65.21, X'28B0', 16.35, NULL, 'doers'); | > | 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | # if {![info exists testdir]} { set testdir [file join [file dirname [info script]] .. .. test] } source [file join [file dirname [info script]] session_common.tcl] source $testdir/tester.tcl ifcapable !session {finish_test; return} set testprefix session4 do_test 1.0 { execsql { CREATE TABLE x(a, b, c, d, e, PRIMARY KEY(c, e)); INSERT INTO x VALUES(65.21, X'28B0', 16.35, NULL, 'doers'); |
︙ | ︙ |
Changes to ext/session/sqlite3session.c.
1 |
| | | 1 2 3 4 5 6 7 8 9 | #if defined(SQLITE_ENABLE_SESSION) && defined(SQLITE_ENABLE_PREUPDATE_HOOK) #include "sqlite3session.h" #include <assert.h> #include <string.h> #include "sqliteInt.h" #include "vdbeInt.h" |
︙ | ︙ | |||
2529 2530 2531 2532 2533 2534 2535 | sqlite3_finalize(sApply.pUpdate); sqlite3_finalize(sApply.pSelect); sqlite3_free(sApply.azCol); sqlite3_mutex_leave(sqlite3_db_mutex(db)); return rc; } | | | 2529 2530 2531 2532 2533 2534 2535 2536 | sqlite3_finalize(sApply.pUpdate); sqlite3_finalize(sApply.pSelect); sqlite3_free(sApply.azCol); sqlite3_mutex_leave(sqlite3_db_mutex(db)); return rc; } #endif /* SQLITE_ENABLE_SESSION && SQLITE_ENABLE_PREUPDATE_HOOK */ |
Changes to ext/session/test_session.c.
1 |
| | > | 1 2 3 4 5 6 7 8 9 10 | #if defined(SQLITE_TEST) && defined(SQLITE_ENABLE_SESSION) \ && defined(SQLITE_ENABLE_PREUPDATE_HOOK) #include "sqlite3session.h" #include <assert.h> #include <string.h> #include <tcl.h> static int test_session_error(Tcl_Interp *interp, int rc){ |
︙ | ︙ | |||
515 516 517 518 519 520 521 | ); Tcl_CreateObjCommand( interp, "sqlite3changeset_apply", test_sqlite3changeset_apply, 0, 0 ); return TCL_OK; } | < | | 516 517 518 519 520 521 522 523 | ); Tcl_CreateObjCommand( interp, "sqlite3changeset_apply", test_sqlite3changeset_apply, 0, 0 ); return TCL_OK; } #endif /* SQLITE_TEST && SQLITE_SESSION && SQLITE_PREUPDATE_HOOK */ |
Changes to src/delete.c.
︙ | ︙ | |||
340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 | ** this optimization caused the row change count (the value returned by ** API function sqlite3_count_changes) to be set incorrectly. */ if( rcauth==SQLITE_OK && pWhere==0 && !pTrigger && !IsVirtual(pTab) && db->xPreUpdateCallback==0 && 0==sqlite3FkRequired(pParse, pTab, 0, 0) ){ assert( !isView ); sqlite3VdbeAddOp4(v, OP_Clear, pTab->tnum, iDb, memCnt, pTab->zName, P4_STATIC); for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ assert( pIdx->pSchema==pTab->pSchema ); | > > | 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 | ** this optimization caused the row change count (the value returned by ** API function sqlite3_count_changes) to be set incorrectly. */ if( rcauth==SQLITE_OK && pWhere==0 && !pTrigger && !IsVirtual(pTab) #ifdef SQLITE_ENABLE_PREUPDATE_HOOK && db->xPreUpdateCallback==0 #endif && 0==sqlite3FkRequired(pParse, pTab, 0, 0) ){ assert( !isView ); sqlite3VdbeAddOp4(v, OP_Clear, pTab->tnum, iDb, memCnt, pTab->zName, P4_STATIC); for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ assert( pIdx->pSchema==pTab->pSchema ); |
︙ | ︙ |
Changes to src/insert.c.
︙ | ︙ | |||
1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 | } if( pTrigger || sqlite3FkRequired(pParse, pTab, 0, 0) ){ sqlite3MultiWrite(pParse); sqlite3GenerateRowDelete( pParse, pTab, baseCur, regRowid, 0, pTrigger, OE_Replace ); }else{ /* This OP_Delete opcode fires the pre-update-hook only. It does ** not modify the b-tree. It is more efficient to let the coming ** OP_Insert replace the existing entry than it is to delete the ** existing entry and then insert a new one. */ sqlite3VdbeAddOp2(v, OP_Delete, baseCur, OPFLAG_ISNOOP); sqlite3VdbeChangeP4(v, -1, (char *)pTab, P4_TABLE); if( pTab->pIndex ){ sqlite3MultiWrite(pParse); sqlite3GenerateRowIndexDelete(pParse, pTab, baseCur, 0); } } seenReplace = 1; break; | > > | 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 | } if( pTrigger || sqlite3FkRequired(pParse, pTab, 0, 0) ){ sqlite3MultiWrite(pParse); sqlite3GenerateRowDelete( pParse, pTab, baseCur, regRowid, 0, pTrigger, OE_Replace ); }else{ #ifdef SQLITE_ENABLE_PREUPDATE_HOOK /* This OP_Delete opcode fires the pre-update-hook only. It does ** not modify the b-tree. It is more efficient to let the coming ** OP_Insert replace the existing entry than it is to delete the ** existing entry and then insert a new one. */ sqlite3VdbeAddOp2(v, OP_Delete, baseCur, OPFLAG_ISNOOP); sqlite3VdbeChangeP4(v, -1, (char *)pTab, P4_TABLE); #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ if( pTab->pIndex ){ sqlite3MultiWrite(pParse); sqlite3GenerateRowIndexDelete(pParse, pTab, baseCur, 0); } } seenReplace = 1; break; |
︙ | ︙ |
Changes to src/main.c.
︙ | ︙ | |||
1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 | pRet = db->pRollbackArg; db->xRollbackCallback = xCallback; db->pRollbackArg = pArg; sqlite3_mutex_leave(db->mutex); return pRet; } /* ** Register a callback to be invoked each time a row is updated, ** inserted or deleted using this database connection. */ void *sqlite3_preupdate_hook( sqlite3 *db, /* Attach the hook to this database */ void(*xCallback)( /* Callback function */ void*,sqlite3*,int,char const*,char const*,sqlite3_int64,sqlite3_int64), void *pArg /* First callback argument */ ){ void *pRet; sqlite3_mutex_enter(db->mutex); pRet = db->pPreUpdateArg; db->xPreUpdateCallback = xCallback; db->pPreUpdateArg = pArg; sqlite3_mutex_leave(db->mutex); return pRet; } #ifndef SQLITE_OMIT_WAL /* ** The sqlite3_wal_hook() callback registered by sqlite3_wal_autocheckpoint(). ** Invoke sqlite3_wal_checkpoint if the number of frames in the log file ** is greater than sqlite3.pWalArg cast to an integer (the value configured by ** wal_autocheckpoint()). | > > | 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 | pRet = db->pRollbackArg; db->xRollbackCallback = xCallback; db->pRollbackArg = pArg; sqlite3_mutex_leave(db->mutex); return pRet; } #ifdef SQLITE_ENABLE_PREUPDATE_HOOK /* ** Register a callback to be invoked each time a row is updated, ** inserted or deleted using this database connection. */ void *sqlite3_preupdate_hook( sqlite3 *db, /* Attach the hook to this database */ void(*xCallback)( /* Callback function */ void*,sqlite3*,int,char const*,char const*,sqlite3_int64,sqlite3_int64), void *pArg /* First callback argument */ ){ void *pRet; sqlite3_mutex_enter(db->mutex); pRet = db->pPreUpdateArg; db->xPreUpdateCallback = xCallback; db->pPreUpdateArg = pArg; sqlite3_mutex_leave(db->mutex); return pRet; } #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ #ifndef SQLITE_OMIT_WAL /* ** The sqlite3_wal_hook() callback registered by sqlite3_wal_autocheckpoint(). ** Invoke sqlite3_wal_checkpoint if the number of frames in the log file ** is greater than sqlite3.pWalArg cast to an integer (the value configured by ** wal_autocheckpoint()). |
︙ | ︙ |
Changes to src/sqlite.h.in.
︙ | ︙ | |||
6387 6388 6389 6390 6391 6392 6393 6394 6395 6396 6397 6398 6399 6400 | #define SQLITE_CHECKPOINT_FULL 1 #define SQLITE_CHECKPOINT_RESTART 2 /* ** CAPI3REF: The pre-update hook. ** EXPERIMENTAL ** ** ^The [sqlite3_preupdate_hook()] interface registers a callback function ** that is invoked prior to each [INSERT], [UPDATE], and [DELETE] operation. ** ^At most one preupdate hook may be registered at a time on a single ** [database connection]; each call to [sqlite3_preupdate_hook()] overrides ** the previous setting. ** ^The preupdate hook is disabled by invoking [sqlite3_preupdate_hook()] | > > > | 6387 6388 6389 6390 6391 6392 6393 6394 6395 6396 6397 6398 6399 6400 6401 6402 6403 | #define SQLITE_CHECKPOINT_FULL 1 #define SQLITE_CHECKPOINT_RESTART 2 /* ** CAPI3REF: The pre-update hook. ** EXPERIMENTAL ** ** ^These interfaces are only available if SQLite is compiled using the ** [SQLITE_ENABLE_UPDATE_HOOK] compile-time option. ** ** ^The [sqlite3_preupdate_hook()] interface registers a callback function ** that is invoked prior to each [INSERT], [UPDATE], and [DELETE] operation. ** ^At most one preupdate hook may be registered at a time on a single ** [database connection]; each call to [sqlite3_preupdate_hook()] overrides ** the previous setting. ** ^The preupdate hook is disabled by invoking [sqlite3_preupdate_hook()] |
︙ | ︙ |
Changes to src/sqliteInt.h.
︙ | ︙ | |||
825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 | void *pProfileArg; /* Argument to profile function */ 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 *pPreUpdateArg; /* First argument to xPreUpdateCallback */ void (*xPreUpdateCallback)( /* Registered using sqlite3_preupdate_hook() */ void*,sqlite3*,int,char const*,char const*,sqlite3_int64,sqlite3_int64 ); PreUpdate *pPreUpdate; /* Context for active pre-update callback */ #ifndef SQLITE_OMIT_WAL int (*xWalCallback)(void *, sqlite3 *, const char *, int); void *pWalArg; #endif void(*xCollNeeded)(void*,sqlite3*,int eTextRep,const char*); void(*xCollNeeded16)(void*,sqlite3*,int eTextRep,const void*); void *pCollNeededArg; | > > | 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 | void *pProfileArg; /* Argument to profile function */ 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); #ifdef SQLITE_ENABLE_PREUPDATE_HOOK void *pPreUpdateArg; /* First argument to xPreUpdateCallback */ void (*xPreUpdateCallback)( /* Registered using sqlite3_preupdate_hook() */ void*,sqlite3*,int,char const*,char const*,sqlite3_int64,sqlite3_int64 ); PreUpdate *pPreUpdate; /* Context for active pre-update callback */ #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ #ifndef SQLITE_OMIT_WAL int (*xWalCallback)(void *, sqlite3 *, const char *, int); void *pWalArg; #endif void(*xCollNeeded)(void*,sqlite3*,int eTextRep,const char*); void(*xCollNeeded16)(void*,sqlite3*,int eTextRep,const void*); void *pCollNeededArg; |
︙ | ︙ |
Changes to src/tclsqlite.c.
︙ | ︙ | |||
649 650 651 652 653 654 655 656 657 658 659 660 661 662 | Tcl_EvalObjEx(pDb->interp, pDb->pUnlockNotify, flags); Tcl_DecrRefCount(pDb->pUnlockNotify); pDb->pUnlockNotify = 0; } } #endif /* ** Pre-update hook callback. */ static void DbPreUpdateHandler( void *p, sqlite3 *db, int op, | > | 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 | Tcl_EvalObjEx(pDb->interp, pDb->pUnlockNotify, flags); Tcl_DecrRefCount(pDb->pUnlockNotify); pDb->pUnlockNotify = 0; } } #endif #ifdef SQLITE_ENABLE_PREUPDATE_HOOK /* ** Pre-update hook callback. */ static void DbPreUpdateHandler( void *p, sqlite3 *db, int op, |
︙ | ︙ | |||
682 683 684 685 686 687 688 689 690 691 692 693 694 695 | Tcl_ListObjAppendElement(0, pCmd, Tcl_NewStringObj(zDb, -1)); Tcl_ListObjAppendElement(0, pCmd, Tcl_NewStringObj(zTbl, -1)); Tcl_ListObjAppendElement(0, pCmd, Tcl_NewWideIntObj(iKey1)); Tcl_ListObjAppendElement(0, pCmd, Tcl_NewWideIntObj(iKey2)); Tcl_EvalObjEx(pDb->interp, pCmd, TCL_EVAL_DIRECT); Tcl_DecrRefCount(pCmd); } static void DbUpdateHandler( void *p, int op, const char *zDb, const char *zTbl, sqlite_int64 rowid | > | 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 | Tcl_ListObjAppendElement(0, pCmd, Tcl_NewStringObj(zDb, -1)); Tcl_ListObjAppendElement(0, pCmd, Tcl_NewStringObj(zTbl, -1)); Tcl_ListObjAppendElement(0, pCmd, Tcl_NewWideIntObj(iKey1)); Tcl_ListObjAppendElement(0, pCmd, Tcl_NewWideIntObj(iKey2)); Tcl_EvalObjEx(pDb->interp, pCmd, TCL_EVAL_DIRECT); Tcl_DecrRefCount(pCmd); } #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ static void DbUpdateHandler( void *p, int op, const char *zDb, const char *zTbl, sqlite_int64 rowid |
︙ | ︙ | |||
1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 | assert( !(*ppHook) ); if( Tcl_GetCharLength(pArg)>0 ){ *ppHook = pArg; Tcl_IncrRefCount(*ppHook); } } sqlite3_preupdate_hook(db, (pDb->pPreUpdateHook?DbPreUpdateHandler:0), pDb); sqlite3_update_hook(db, (pDb->pUpdateHook?DbUpdateHandler:0), pDb); sqlite3_rollback_hook(db, (pDb->pRollbackHook?DbRollbackHandler:0), pDb); sqlite3_wal_hook(db, (pDb->pWalHook?DbWalHandler:0), pDb); } /* ** The "sqlite" command below creates a new Tcl command for each | > > | 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 | assert( !(*ppHook) ); if( Tcl_GetCharLength(pArg)>0 ){ *ppHook = pArg; Tcl_IncrRefCount(*ppHook); } } #ifdef SQLITE_ENABLE_PREUPDATE_HOOK sqlite3_preupdate_hook(db, (pDb->pPreUpdateHook?DbPreUpdateHandler:0), pDb); #endif sqlite3_update_hook(db, (pDb->pUpdateHook?DbUpdateHandler:0), pDb); sqlite3_rollback_hook(db, (pDb->pRollbackHook?DbRollbackHandler:0), pDb); sqlite3_wal_hook(db, (pDb->pWalHook?DbWalHandler:0), pDb); } /* ** The "sqlite" command below creates a new Tcl command for each |
︙ | ︙ | |||
2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 | /* ** $db preupdate_hook count ** $db preupdate_hook hook ?SCRIPT? ** $db preupdate_hook new INDEX ** $db preupdate_hook old INDEX */ case DB_PREUPDATE: { static const char *azSub[] = {"count", "depth", "hook", "new", "old", 0}; enum DbPreupdateSubCmd { PRE_COUNT, PRE_DEPTH, PRE_HOOK, PRE_NEW, PRE_OLD }; int iSub; if( objc<3 ){ | > > > > | 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 | /* ** $db preupdate_hook count ** $db preupdate_hook hook ?SCRIPT? ** $db preupdate_hook new INDEX ** $db preupdate_hook old INDEX */ case DB_PREUPDATE: { #ifndef SQLITE_ENABLE_PREUPDATE_HOOK Tcl_AppendResult(interp, "preupdate_hook was omitted at compile-time"); rc = TCL_ERROR; #else static const char *azSub[] = {"count", "depth", "hook", "new", "old", 0}; enum DbPreupdateSubCmd { PRE_COUNT, PRE_DEPTH, PRE_HOOK, PRE_NEW, PRE_OLD }; int iSub; if( objc<3 ){ |
︙ | ︙ | |||
2916 2917 2918 2919 2920 2921 2922 | Tcl_SetObjResult(interp, pObj); }else{ Tcl_AppendResult(interp, sqlite3_errmsg(pDb->db), 0); return TCL_ERROR; } } } | | | 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 | Tcl_SetObjResult(interp, pObj); }else{ Tcl_AppendResult(interp, sqlite3_errmsg(pDb->db), 0); return TCL_ERROR; } } } #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ break; } /* ** $db wal_hook ?script? ** $db update_hook ?script? ** $db rollback_hook ?script? |
︙ | ︙ | |||
3716 3717 3718 3719 3720 3721 3722 | extern int Sqlitetestvfs_Init(Tcl_Interp *); extern int SqlitetestStat_Init(Tcl_Interp*); extern int Sqlitetestrtree_Init(Tcl_Interp*); extern int Sqlitequota_Init(Tcl_Interp*); extern int Sqlitemultiplex_Init(Tcl_Interp*); extern int SqliteSuperlock_Init(Tcl_Interp*); extern int SqlitetestSyscall_Init(Tcl_Interp*); | | | 3724 3725 3726 3727 3728 3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 | extern int Sqlitetestvfs_Init(Tcl_Interp *); extern int SqlitetestStat_Init(Tcl_Interp*); extern int Sqlitetestrtree_Init(Tcl_Interp*); extern int Sqlitequota_Init(Tcl_Interp*); extern int Sqlitemultiplex_Init(Tcl_Interp*); extern int SqliteSuperlock_Init(Tcl_Interp*); extern int SqlitetestSyscall_Init(Tcl_Interp*); #if defined(SQLITE_ENABLE_SESSION) && defined(SQLITE_ENABLE_PREUPDATE_HOOK) extern int TestSession_Init(Tcl_Interp*); #endif #ifdef SQLITE_ENABLE_ZIPVFS extern int Zipvfs_Init(Tcl_Interp*); Zipvfs_Init(interp); #endif |
︙ | ︙ | |||
3756 3757 3758 3759 3760 3761 3762 | Sqlitetestvfs_Init(interp); SqlitetestStat_Init(interp); Sqlitetestrtree_Init(interp); Sqlitequota_Init(interp); Sqlitemultiplex_Init(interp); SqliteSuperlock_Init(interp); SqlitetestSyscall_Init(interp); | | | 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 | Sqlitetestvfs_Init(interp); SqlitetestStat_Init(interp); Sqlitetestrtree_Init(interp); Sqlitequota_Init(interp); Sqlitemultiplex_Init(interp); SqliteSuperlock_Init(interp); SqlitetestSyscall_Init(interp); #if defined(SQLITE_ENABLE_SESSION) && defined(SQLITE_ENABLE_PREUPDATE_HOOK) TestSession_Init(interp); #endif Tcl_CreateObjCommand(interp,"load_testfixture_extensions",init_all_cmd,0,0); #ifdef SQLITE_SSE Sqlitetestsse_Init(interp); |
︙ | ︙ |
Changes to src/test_config.c.
︙ | ︙ | |||
80 81 82 83 84 85 86 87 88 89 90 91 92 93 | #endif #ifdef SQLITE_ENABLE_MEMSYS5 Tcl_SetVar2(interp, "sqlite_options", "mem5", "1", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "mem5", "0", TCL_GLOBAL_ONLY); #endif #ifdef SQLITE_MUTEX_OMIT Tcl_SetVar2(interp, "sqlite_options", "mutex", "0", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "mutex", "1", TCL_GLOBAL_ONLY); #endif | > > > > > > | 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 | #endif #ifdef SQLITE_ENABLE_MEMSYS5 Tcl_SetVar2(interp, "sqlite_options", "mem5", "1", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "mem5", "0", TCL_GLOBAL_ONLY); #endif #ifdef SQLITE_ENABLE_PREUPDATE_HOOK Tcl_SetVar2(interp, "sqlite_options", "preupdate", "1", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "preupdate", "0", TCL_GLOBAL_ONLY); #endif #ifdef SQLITE_MUTEX_OMIT Tcl_SetVar2(interp, "sqlite_options", "mutex", "0", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "mutex", "1", TCL_GLOBAL_ONLY); #endif |
︙ | ︙ |
Changes to src/update.c.
︙ | ︙ | |||
483 484 485 486 487 488 489 | if( hasFK ){ sqlite3FkCheck(pParse, pTab, regOldRowid, 0); } /* Delete the index entries associated with the current record. */ j1 = sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, regOldRowid); sqlite3GenerateRowIndexDelete(pParse, pTab, iCur, aRegIdx); | | | | 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 | if( hasFK ){ sqlite3FkCheck(pParse, pTab, regOldRowid, 0); } /* Delete the index entries associated with the current record. */ j1 = sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, regOldRowid); sqlite3GenerateRowIndexDelete(pParse, pTab, iCur, aRegIdx); /* If changing the rowid value, or if there are foreign key constraints ** to process, delete the old record. Otherwise, add a noop OP_Delete ** to invoke the pre-update hook. ** ** That (regNew==regnewRowid+1) is true is also important for the ** pre-update hook. If the caller invokes preupdate_new(), the returned ** value is copied from memory cell (regNewRowid+1+iCol), where iCol ** is the column index supplied by the user. */ assert( regNew==regNewRowid+1 ); sqlite3VdbeAddOp3(v, OP_Delete, iCur, OPFLAG_ISUPDATE | ((hasFK || chngRowid) ? 0 : OPFLAG_ISNOOP), regNewRowid |
︙ | ︙ |
Changes to src/vdbe.c.
︙ | ︙ | |||
103 104 105 106 107 108 109 110 111 112 113 114 115 116 | static void updateMaxBlobsize(Mem *p){ if( (p->flags & (MEM_Str|MEM_Blob))!=0 && p->n>sqlite3_max_blobsize ){ sqlite3_max_blobsize = p->n; } } #endif /* ** The next global variable is incremented each type the OP_Found opcode ** is executed. This is used to test whether or not the foreign key ** operation implemented using OP_FkIsZero is working. This variable ** has no function other than to help verify the correct operation of the ** library. */ | > > > > > > > > > > | 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | static void updateMaxBlobsize(Mem *p){ if( (p->flags & (MEM_Str|MEM_Blob))!=0 && p->n>sqlite3_max_blobsize ){ sqlite3_max_blobsize = p->n; } } #endif /* ** This macro evaluates to true if either the update hook or the preupdate ** hook are enabled for database connect DB. */ #ifdef SQLITE_ENABLE_PREUPDATE_HOOK # define HAS_UPDATE_HOOK(DB) ((DB)->xPreUpdateCallback||(DB)->xUpdateCallback) #else # define HAS_UPDATE_HOOK(DB) ((DB)->xUpdateCallback) #endif /* ** The next global variable is incremented each type the OP_Found opcode ** is executed. This is used to test whether or not the foreign key ** operation implemented using OP_FkIsZero is working. This variable ** has no function other than to help verify the correct operation of the ** library. */ |
︙ | ︙ | |||
3886 3887 3888 3889 3890 3891 3892 | REGISTER_TRACE(pOp->p3, pKey); iKey = pKey->u.i; }else{ assert( pOp->opcode==OP_InsertInt ); iKey = pOp->p3; } | | > > | 3896 3897 3898 3899 3900 3901 3902 3903 3904 3905 3906 3907 3908 3909 3910 3911 3912 3913 3914 3915 3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 | REGISTER_TRACE(pOp->p3, pKey); iKey = pKey->u.i; }else{ assert( pOp->opcode==OP_InsertInt ); iKey = pOp->p3; } if( pOp->p4type==P4_TABLE && HAS_UPDATE_HOOK(db) ){ assert( pC->isTable ); assert( pC->iDb>=0 ); zDb = db->aDb[pC->iDb].zName; pTab = pOp->p4.pTab; op = ((pOp->p5 & OPFLAG_ISUPDATE) ? SQLITE_UPDATE : SQLITE_INSERT); } #ifdef SQLITE_ENABLE_PREUPDATE_HOOK /* Invoke the pre-update hook, if any */ if( db->xPreUpdateCallback && pOp->p4type==P4_TABLE && (!(pOp->p5 & OPFLAG_ISUPDATE) || pC->rowidIsValid==0) ){ sqlite3VdbePreUpdateHook(p, pC, SQLITE_INSERT, zDb, pTab, iKey, pOp->p2); } #endif if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++; if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = iKey; if( pData->flags & MEM_Null ){ pData->z = 0; pData->n = 0; }else{ |
︙ | ︙ | |||
3987 3988 3989 3990 3991 3992 3993 | assert( pC->deferredMoveto==0 ); rc = sqlite3VdbeCursorMoveto(pC); if( NEVER(rc!=SQLITE_OK) ) goto abort_due_to_error; /* If the update-hook or pre-update-hook will be invoked, set iKey to ** the rowid of the row being deleted. Set zDb and zTab as well. */ | | > > | 3999 4000 4001 4002 4003 4004 4005 4006 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017 4018 4019 4020 4021 4022 4023 4024 4025 4026 4027 4028 4029 4030 4031 4032 | assert( pC->deferredMoveto==0 ); rc = sqlite3VdbeCursorMoveto(pC); if( NEVER(rc!=SQLITE_OK) ) goto abort_due_to_error; /* If the update-hook or pre-update-hook will be invoked, set iKey to ** the rowid of the row being deleted. Set zDb and zTab as well. */ if( pOp->p4.z && HAS_UPDATE_HOOK(db) ){ assert( pC->iDb>=0 ); assert( pC->isTable ); assert( pC->rowidIsValid ); /* lastRowid set by previous OP_NotFound */ iKey = pC->lastRowid; zDb = db->aDb[pC->iDb].zName; pTab = pOp->p4.pTab; } #ifdef SQLITE_ENABLE_PREUPDATE_HOOK /* Invoke the pre-update-hook if required. */ if( db->xPreUpdateCallback && pOp->p4.z ){ assert( !(opflags & OPFLAG_ISUPDATE) || (aMem[pOp->p3].flags & MEM_Int) ); sqlite3VdbePreUpdateHook(p, pC, (opflags & OPFLAG_ISUPDATE) ? SQLITE_UPDATE : SQLITE_DELETE, zDb, pTab, iKey, pOp->p3 ); } #endif if( opflags & OPFLAG_ISNOOP ) break; sqlite3BtreeSetCachedRowid(pC->pCursor, 0); rc = sqlite3BtreeDelete(pC->pCursor); pC->cacheStatus = CACHE_STALE; |
︙ | ︙ |
Changes to src/vdbeapi.c.
︙ | ︙ | |||
1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 | int sqlite3_stmt_status(sqlite3_stmt *pStmt, int op, int resetFlag){ Vdbe *pVdbe = (Vdbe*)pStmt; int v = pVdbe->aCounter[op-1]; if( resetFlag ) pVdbe->aCounter[op-1] = 0; return v; } /* ** This function is called from within a pre-update callback to retrieve ** a field of the row currently being updated or deleted. */ int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppValue){ PreUpdate *p = db->pPreUpdate; int rc = SQLITE_OK; | > | 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 | int sqlite3_stmt_status(sqlite3_stmt *pStmt, int op, int resetFlag){ Vdbe *pVdbe = (Vdbe*)pStmt; int v = pVdbe->aCounter[op-1]; if( resetFlag ) pVdbe->aCounter[op-1] = 0; return v; } #ifdef SQLITE_ENABLE_PREUPDATE_HOOK /* ** This function is called from within a pre-update callback to retrieve ** a field of the row currently being updated or deleted. */ int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppValue){ PreUpdate *p = db->pPreUpdate; int rc = SQLITE_OK; |
︙ | ︙ | |||
1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 | sqlite3VdbeMemStoreType(*ppValue); } preupdate_old_out: sqlite3Error(db, rc, 0); return sqlite3ApiExit(db, rc); } /* ** This function is called from within a pre-update callback to retrieve ** the number of columns in the row being updated, deleted or inserted. */ int sqlite3_preupdate_count(sqlite3 *db){ PreUpdate *p = db->pPreUpdate; return (p ? p->pCsr->nField : 0); } /* ** This function is designed to be called from within a pre-update callback ** only. It returns zero if the change that caused the callback was made ** immediately by a user SQL statement. Or, if the change was made by a ** trigger program, it returns the number of trigger programs currently ** on the stack (1 for a top-level trigger, 2 for a trigger fired by a ** top-level trigger etc.). ** ** For the purposes of the previous paragraph, a foreign key CASCADE, SET NULL ** or SET DEFAULT action is considered a trigger. */ int sqlite3_preupdate_depth(sqlite3 *db){ PreUpdate *p = db->pPreUpdate; return (p ? p->v->nFrame : 0); } /* ** This function is called from within a pre-update callback to retrieve ** a field of the row currently being updated or inserted. */ int sqlite3_preupdate_new(sqlite3 *db, int iIdx, sqlite3_value **ppValue){ PreUpdate *p = db->pPreUpdate; int rc = SQLITE_OK; | > > > > > > | 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 | sqlite3VdbeMemStoreType(*ppValue); } preupdate_old_out: sqlite3Error(db, rc, 0); return sqlite3ApiExit(db, rc); } #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ #ifdef SQLITE_ENABLE_PREUPDATE_HOOK /* ** This function is called from within a pre-update callback to retrieve ** the number of columns in the row being updated, deleted or inserted. */ int sqlite3_preupdate_count(sqlite3 *db){ PreUpdate *p = db->pPreUpdate; return (p ? p->pCsr->nField : 0); } #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ #ifdef SQLITE_ENABLE_PREUPDATE_HOOK /* ** This function is designed to be called from within a pre-update callback ** only. It returns zero if the change that caused the callback was made ** immediately by a user SQL statement. Or, if the change was made by a ** trigger program, it returns the number of trigger programs currently ** on the stack (1 for a top-level trigger, 2 for a trigger fired by a ** top-level trigger etc.). ** ** For the purposes of the previous paragraph, a foreign key CASCADE, SET NULL ** or SET DEFAULT action is considered a trigger. */ int sqlite3_preupdate_depth(sqlite3 *db){ PreUpdate *p = db->pPreUpdate; return (p ? p->v->nFrame : 0); } #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ #ifdef SQLITE_ENABLE_PREUPDATE_HOOK /* ** This function is called from within a pre-update callback to retrieve ** a field of the row currently being updated or inserted. */ int sqlite3_preupdate_new(sqlite3 *db, int iIdx, sqlite3_value **ppValue){ PreUpdate *p = db->pPreUpdate; int rc = SQLITE_OK; |
︙ | ︙ | |||
1477 1478 1479 1480 1481 1482 1483 | } *ppValue = pMem; preupdate_new_out: sqlite3Error(db, rc, 0); return sqlite3ApiExit(db, rc); } | > | 1484 1485 1486 1487 1488 1489 1490 1491 | } *ppValue = pMem; preupdate_new_out: sqlite3Error(db, rc, 0); return sqlite3ApiExit(db, rc); } #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ |
Changes to src/vdbeaux.c.
︙ | ︙ | |||
2808 2809 2810 2811 2812 2813 2814 | while( idx<szHdr && u<p->nField && d<=nKey ){ u32 serial_type; idx += getVarint32(&aKey[idx], serial_type); pMem->enc = pKeyInfo->enc; pMem->db = pKeyInfo->db; pMem->flags = 0; | | | 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 | while( idx<szHdr && u<p->nField && d<=nKey ){ u32 serial_type; idx += getVarint32(&aKey[idx], serial_type); pMem->enc = pKeyInfo->enc; pMem->db = pKeyInfo->db; pMem->flags = 0; pMem->zMalloc = 0; pMem->z = 0; d += sqlite3VdbeSerialGet(&aKey[d], serial_type, pMem); pMem++; u++; } assert( u<=pKeyInfo->nField + 1 ); p->nField = u; |
︙ | ︙ | |||
3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 | if( iVar>32 ){ v->expmask = 0xffffffff; }else{ v->expmask |= ((u32)1 << (iVar-1)); } } /* ** Invoke the pre-update hook. If this is an UPDATE or DELETE pre-update call, ** then cursor passed as the second argument should point to the row about ** to be update or deleted. If the application calls sqlite3_preupdate_old(), ** the required value will be read from the row the cursor points to. */ void sqlite3VdbePreUpdateHook( | > | 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 | if( iVar>32 ){ v->expmask = 0xffffffff; }else{ v->expmask |= ((u32)1 << (iVar-1)); } } #ifdef SQLITE_ENABLE_PREUPDATE_HOOK /* ** Invoke the pre-update hook. If this is an UPDATE or DELETE pre-update call, ** then cursor passed as the second argument should point to the row about ** to be update or deleted. If the application calls sqlite3_preupdate_old(), ** the required value will be read from the row the cursor points to. */ void sqlite3VdbePreUpdateHook( |
︙ | ︙ | |||
3224 3225 3226 3227 3228 3229 3230 | int i; for(i=0; i<pCsr->nField; i++){ sqlite3VdbeMemRelease(&preupdate.aNew[i]); } sqlite3DbFree(db, preupdate.aNew); } } | > | 3225 3226 3227 3228 3229 3230 3231 3232 | int i; for(i=0; i<pCsr->nField; i++){ sqlite3VdbeMemRelease(&preupdate.aNew[i]); } sqlite3DbFree(db, preupdate.aNew); } } #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ |
Changes to test/hook.test.
︙ | ︙ | |||
360 361 362 363 364 365 366 367 368 369 370 371 372 373 | do_test hook-6.2 { set ::hooks } {COMMIT ROLLBACK} unset ::hooks #---------------------------------------------------------------------------- # The following tests - hook-7.* - test the pre-update hook. # # 7.1.1 - INSERT statement. # 7.1.2 - INSERT INTO ... SELECT statement. # 7.1.3 - REPLACE INTO ... (rowid conflict) # 7.1.4 - REPLACE INTO ... (other index conflicts) # 7.1.5 - REPLACE INTO ... (both rowid and other index conflicts) # | > > > > > | 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 | do_test hook-6.2 { set ::hooks } {COMMIT ROLLBACK} unset ::hooks #---------------------------------------------------------------------------- # The following tests - hook-7.* - test the pre-update hook. # ifcapable !preupdate { finish_test return } # # 7.1.1 - INSERT statement. # 7.1.2 - INSERT INTO ... SELECT statement. # 7.1.3 - REPLACE INTO ... (rowid conflict) # 7.1.4 - REPLACE INTO ... (other index conflicts) # 7.1.5 - REPLACE INTO ... (both rowid and other index conflicts) # |
︙ | ︙ |