SQLite

Check-in [6634521461]
Login

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: 6634521461e6acff7cc778590e62d57831f9230d
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
Unified Diff Ignore Whitespace Patch
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
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








<
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
180
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
2
3
4
5
6
7
8
9

#ifdef SQLITE_ENABLE_SESSION

#include "sqlite3session.h"
#include <assert.h>
#include <string.h>

#include "sqliteInt.h"
#include "vdbeInt.h"

|







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
2536
  sqlite3_finalize(sApply.pUpdate);
  sqlite3_finalize(sApply.pSelect);
  sqlite3_free(sApply.azCol);
  sqlite3_mutex_leave(sqlite3_db_mutex(db));
  return rc;
}

#endif        /* #ifdef SQLITE_ENABLE_SESSION */







|
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
2

3
4
5
6
7
8
9

#if defined(SQLITE_TEST) && defined(SQLITE_ENABLE_SESSION)


#include "sqlite3session.h"
#include <assert.h>
#include <string.h>
#include <tcl.h>

static int test_session_error(Tcl_Interp *interp, int rc){

|
>







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
522
523
  );
  Tcl_CreateObjCommand(
      interp, "sqlite3changeset_apply", test_sqlite3changeset_apply, 0, 0
  );
  return TCL_OK;
}

#endif








<
|
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
2923
2924
2925
2926
2927
2928
2929
2930
          Tcl_SetObjResult(interp, pObj);
        }else{
          Tcl_AppendResult(interp, sqlite3_errmsg(pDb->db), 0);
          return TCL_ERROR;
        }
      }
    }

    break;
  }

  /*
  **    $db wal_hook ?script?
  **    $db update_hook ?script?
  **    $db rollback_hook ?script?







|







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
3723
3724
3725
3726
3727
3728
3729
3730
    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*);
#ifdef SQLITE_ENABLE_SESSION
    extern int TestSession_Init(Tcl_Interp*);
#endif
#ifdef SQLITE_ENABLE_ZIPVFS
    extern int Zipvfs_Init(Tcl_Interp*);
    Zipvfs_Init(interp);
#endif








|







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
3763
3764
3765
3766
3767
3768
3769
3770
    Sqlitetestvfs_Init(interp);
    SqlitetestStat_Init(interp);
    Sqlitetestrtree_Init(interp);
    Sqlitequota_Init(interp);
    Sqlitemultiplex_Init(interp);
    SqliteSuperlock_Init(interp);
    SqlitetestSyscall_Init(interp);
#ifdef SQLITE_ENABLE_SESSION
    TestSession_Init(interp);
#endif

    Tcl_CreateObjCommand(interp,"load_testfixture_extensions",init_all_cmd,0,0);

#ifdef SQLITE_SSE
    Sqlitetestsse_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
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 hte 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







|





|







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
3893
3894
3895
3896
3897
3898
3899
3900

3901
3902
3903
3904
3905
3906
3907

3908
3909
3910
3911
3912
3913
3914
    REGISTER_TRACE(pOp->p3, pKey);
    iKey = pKey->u.i;
  }else{
    assert( pOp->opcode==OP_InsertInt );
    iKey = pOp->p3;
  }

  if( pOp->p4type==P4_TABLE && (db->xPreUpdateCallback||db->xUpdateCallback) ){
    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);
  }


  /* 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);
  }


  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{







|







>







>







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
3994
3995
3996
3997
3998
3999
4000
4001
4002

4003
4004
4005
4006
4007
4008
4009
4010
4011

4012
4013
4014
4015
4016
4017
4018
  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 && (db->xPreUpdateCallback || db->xUpdateCallback) ){
    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;
  }


  /* 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
    );
  }


  if( opflags & OPFLAG_ISNOOP ) break;

  sqlite3BtreeSetCachedRowid(pC->pCursor, 0);
  rc = sqlite3BtreeDelete(pC->pCursor);
  pC->cacheStatus = CACHE_STALE;








|








>









>







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
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 = pMem->z = 0;
    pMem->z = 0;
    d += sqlite3VdbeSerialGet(&aKey[d], serial_type, pMem);
    pMem++;
    u++;
  }
  assert( u<=pKeyInfo->nField + 1 );
  p->nField = u;







|







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)
#