/ Check-in [093d8cd8]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Add the experimental sqlite3_transaction_hook() API.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | sessions
Files: files | file ages | folders
SHA1: 093d8cd8e2f3a6af5d40cf810e396f4919eb5cef
User & Date: dan 2011-03-03 20:06:00
Context
2011-03-08
19:22
Add start of sessions feature. check-in: 269a81a3 user: dan tags: sessions
2011-03-03
20:06
Add the experimental sqlite3_transaction_hook() API. check-in: 093d8cd8 user: dan tags: sessions
2011-03-01
18:42
Add the experimental sqlite3_preupdate_hook() API. check-in: 6145d7b8 user: dan tags: sessions
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/main.c.

   760    760     if( db->lookaside.bMalloced ){
   761    761       sqlite3_free(db->lookaside.pStart);
   762    762     }
   763    763     sqlite3_free(db);
   764    764     return SQLITE_OK;
   765    765   }
   766    766   
          767  +
          768  +/*
          769  +** Invoke the transaction-hook.
          770  +*/
          771  +void sqlite3TransactionHook(sqlite3 *db, int op, int iLevel){
          772  +  assert( op==SQLITE_BEGIN || op==SQLITE_COMMIT || op==SQLITE_ROLLBACK );
          773  +  assert( op==SQLITE_BEGIN || iLevel<db->iOpenTrans || iLevel==0 );
          774  +  assert( op==SQLITE_BEGIN || iLevel<db->iOpenTrans || db->iOpenTrans==0 );
          775  +  assert( op!=SQLITE_BEGIN || iLevel==db->iOpenTrans || iLevel==0 );
          776  +  assert( op!=SQLITE_BEGIN || iLevel==db->iOpenTrans || db->iOpenTrans==1 );
          777  +
          778  +  if( op==SQLITE_BEGIN && iLevel!=db->iOpenTrans ) return;
          779  +  if( op!=SQLITE_BEGIN && iLevel>=db->iOpenTrans ) return;
          780  +
          781  +  if( db->xTransCallback ){
          782  +    db->xTransCallback(db->pTransArg, op, iLevel);
          783  +  }
          784  +  db->iOpenTrans = iLevel + (op==SQLITE_BEGIN);
          785  +}
          786  +
   767    787   /*
   768    788   ** Rollback all database files.
   769    789   */
   770    790   void sqlite3RollbackAll(sqlite3 *db){
   771    791     int i;
   772    792     int inTrans = 0;
   773    793     assert( sqlite3_mutex_held(db->mutex) );
................................................................................
   792    812     /* Any deferred constraint violations have now been resolved. */
   793    813     db->nDeferredCons = 0;
   794    814   
   795    815     /* If one has been configured, invoke the rollback-hook callback */
   796    816     if( db->xRollbackCallback && (inTrans || !db->autoCommit) ){
   797    817       db->xRollbackCallback(db->pRollbackArg);
   798    818     }
          819  +
          820  +  /* If a transaction-hook is configured, invoke it now to report on 
          821  +  ** the rollback operation. */
          822  +  sqlite3TransactionHook(db, SQLITE_ROLLBACK, 0);
   799    823   }
   800    824   
   801    825   /*
   802    826   ** Return a static string that describes the kind of error specified in the
   803    827   ** argument.
   804    828   */
   805    829   const char *sqlite3ErrStr(int rc){
................................................................................
  1285   1309     sqlite3_mutex_enter(db->mutex);
  1286   1310     pRet = db->pPreUpdateArg;
  1287   1311     db->xPreUpdateCallback = xCallback;
  1288   1312     db->pPreUpdateArg = pArg;
  1289   1313     sqlite3_mutex_leave(db->mutex);
  1290   1314     return pRet;
  1291   1315   }
         1316  +
         1317  +/*
         1318  +** Register a callback to be invoked each time a transaction or savepoint
         1319  +** is opened, committed or rolled back.
         1320  +*/
         1321  +void *sqlite3_transaction_hook(
         1322  +  sqlite3 *db,                              /* Database handle */
         1323  +  void(*xCallback)(void *, int, int),       /* Callback function */
         1324  +  void *pArg                                /* First callback argument */
         1325  +){
         1326  +  void *pRet;
         1327  +  sqlite3_mutex_enter(db->mutex);
         1328  +  pRet = db->pTransArg;
         1329  +  db->xTransCallback = xCallback;
         1330  +  db->pTransArg = pArg;
         1331  +  sqlite3_mutex_leave(db->mutex);
         1332  +  return pRet;
         1333  +}
  1292   1334   
  1293   1335   #ifndef SQLITE_OMIT_WAL
  1294   1336   /*
  1295   1337   ** The sqlite3_wal_hook() callback registered by sqlite3_wal_autocheckpoint().
  1296   1338   ** Invoke sqlite3_wal_checkpoint if the number of frames in the log file
  1297   1339   ** is greater than sqlite3.pWalArg cast to an integer (the value configured by
  1298   1340   ** wal_autocheckpoint()).

Changes to src/sqlite.h.in.

  6340   6340   ** documentation for additional information about the meaning and use of
  6341   6341   ** each of these values.
  6342   6342   */
  6343   6343   #define SQLITE_CHECKPOINT_PASSIVE 0
  6344   6344   #define SQLITE_CHECKPOINT_FULL    1
  6345   6345   #define SQLITE_CHECKPOINT_RESTART 2
  6346   6346   
  6347         -void *sqlite3_preupdate_hook(
         6347  +
         6348  +/*
         6349  +** CAPI3REF: The pre-update hook.
         6350  +** 
         6351  +** The preupdate_old() API may only be used from within an SQLITE_UPDATE or
         6352  +** SQLITE_DELETE pre-update callback. The preupdate_modified() API may only 
         6353  +** be used from within an SQLITE_UPDATE pre-update callback.
         6354  +*/
         6355  +SQLITE_EXPERIMENTAL void *sqlite3_preupdate_hook(
  6348   6356     sqlite3 *db,
  6349   6357     void(*xPreUpdate)(
  6350   6358       void *pCtx,                   /* Copy of third arg to preupdate_hook() */
  6351   6359       sqlite3 *db,                  /* Database handle */
  6352   6360       int op,                       /* SQLITE_UPDATE, DELETE or INSERT */
  6353   6361       char const *zDb,              /* Database name */
  6354   6362       char const *zName,            /* Table name */
  6355   6363       sqlite3_int64 iKey1,          /* Rowid of row about to be deleted/updated */
  6356   6364       sqlite3_int64 iKey2           /* New rowid value (for a rowid UPDATE) */
  6357   6365     ),
  6358   6366     void*
  6359   6367   );
         6368  +SQLITE_EXPERIMENTAL int sqlite3_preupdate_old(sqlite3 *, int, sqlite3_value **);
         6369  +SQLITE_EXPERIMENTAL int sqlite3_preupdate_modified(sqlite3 *, int, int *);
         6370  +SQLITE_EXPERIMENTAL int sqlite3_preupdate_count(sqlite3 *);
  6360   6371   
  6361   6372   /*
  6362         -** The following APIs may only be used from within a pre-update callback. More
  6363         -** specifically, the preupdate_old() API may only be used from within an
  6364         -** SQLITE_UPDATE or SQLITE_DELETE pre-update callback. The preupdate_modified()
  6365         -** API may only be used from within an SQLITE_UPDATE pre-update callback.
         6373  +** CAPI3REF: The transaction hook.
  6366   6374   */
  6367         -int sqlite3_preupdate_old(sqlite3 *, int, sqlite3_value **);
  6368         -int sqlite3_preupdate_modified(sqlite3 *, int, int *);
  6369         -int sqlite3_preupdate_count(sqlite3 *);
         6375  +SQLITE_EXPERIMENTAL
         6376  +void *sqlite3_transaction_hook(sqlite3 *, void(*)(void *, int, int), void *);
         6377  +
         6378  +#define SQLITE_BEGIN    1
         6379  +#define SQLITE_COMMIT   2
         6380  +#define SQLITE_ROLLBACK 3
  6370   6381   
  6371   6382   
  6372   6383   /*
  6373   6384   ** Undo the hack that converts floating point types to integer for
  6374   6385   ** builds on processors without floating point support.
  6375   6386   */
  6376   6387   #ifdef SQLITE_OMIT_FLOATING_POINT

Changes to src/sqliteInt.h.

   829    829     void *pUpdateArg;
   830    830     void (*xUpdateCallback)(void*,int, const char*,const char*,sqlite_int64);
   831    831     void *pPreUpdateArg;          /* First argument to xPreUpdateCallback */
   832    832     void (*xPreUpdateCallback)(   /* Registered using sqlite3_preupdate_hook() */
   833    833       void*,sqlite3*,int,char const*,char const*,sqlite3_int64,sqlite3_int64
   834    834     );
   835    835     PreUpdate *pPreUpdate;        /* Context for active pre-update callback */
          836  +  void *pTransArg;              /* First argument to xTransCallback */
          837  +  void (*xTransCallback)(void*,int,int);
          838  +  int iOpenTrans;               /* Open transaction (xTransCallback) plus 1 */
   836    839   #ifndef SQLITE_OMIT_WAL
   837    840     int (*xWalCallback)(void *, sqlite3 *, const char *, int);
   838    841     void *pWalArg;
   839    842   #endif
   840    843     void(*xCollNeeded)(void*,sqlite3*,int eTextRep,const char*);
   841    844     void(*xCollNeeded16)(void*,sqlite3*,int eTextRep,const void*);
   842    845     void *pCollNeededArg;
................................................................................
  2744   2747   void sqlite3ExprAnalyzeAggregates(NameContext*, Expr*);
  2745   2748   void sqlite3ExprAnalyzeAggList(NameContext*,ExprList*);
  2746   2749   Vdbe *sqlite3GetVdbe(Parse*);
  2747   2750   void sqlite3PrngSaveState(void);
  2748   2751   void sqlite3PrngRestoreState(void);
  2749   2752   void sqlite3PrngResetState(void);
  2750   2753   void sqlite3RollbackAll(sqlite3*);
         2754  +void sqlite3TransactionHook(sqlite3 *, int, int);
  2751   2755   void sqlite3CodeVerifySchema(Parse*, int);
  2752   2756   void sqlite3BeginTransaction(Parse*, int);
  2753   2757   void sqlite3CommitTransaction(Parse*);
  2754   2758   void sqlite3RollbackTransaction(Parse*);
  2755   2759   void sqlite3Savepoint(Parse*, int, Token*);
  2756   2760   void sqlite3CloseSavepoints(sqlite3 *);
  2757   2761   int sqlite3ExprIsConstant(Expr*);

Changes to src/tclsqlite.c.

   120    120     char *zAuth;               /* The authorization callback routine */
   121    121     int disableAuth;           /* Disable the authorizer if it exists */
   122    122     char *zNull;               /* Text to substitute for an SQL NULL value */
   123    123     SqlFunc *pFunc;            /* List of SQL functions */
   124    124     Tcl_Obj *pUpdateHook;      /* Update hook script (if any) */
   125    125     Tcl_Obj *pPreUpdateHook;   /* Pre-update hook script (if any) */
   126    126     Tcl_Obj *pRollbackHook;    /* Rollback hook script (if any) */
          127  +  Tcl_Obj *pTransHook;       /* Transaction hook script (if any) */
   127    128     Tcl_Obj *pWalHook;         /* WAL hook script (if any) */
   128    129     Tcl_Obj *pUnlockNotify;    /* Unlock notify script (if any) */
   129    130     SqlCollate *pCollate;      /* List of SQL collation functions */
   130    131     int rc;                    /* Return code of most recent sqlite3_exec() */
   131    132     Tcl_Obj *pCollateNeeded;   /* Collation needed script */
   132    133     SqlPreparedStmt *stmtList; /* List of prepared statements*/
   133    134     SqlPreparedStmt *stmtLast; /* Last statement in the list */
................................................................................
   592    593   static void DbRollbackHandler(void *clientData){
   593    594     SqliteDb *pDb = (SqliteDb*)clientData;
   594    595     assert(pDb->pRollbackHook);
   595    596     if( TCL_OK!=Tcl_EvalObjEx(pDb->interp, pDb->pRollbackHook, 0) ){
   596    597       Tcl_BackgroundError(pDb->interp);
   597    598     }
   598    599   }
          600  +
          601  +/*
          602  +** sqlite3_transaction_hook() callback.
          603  +*/
          604  +static void DbTransHandler(void *clientData, int op, int iLevel){
          605  +  static const char *azStr[] = { "BEGIN", "COMMIT", "ROLLBACK" };
          606  +  SqliteDb *pDb = (SqliteDb*)clientData;
          607  +  Tcl_Interp *interp = pDb->interp;
          608  +  Tcl_Obj *pScript;
          609  +
          610  +  assert(pDb->pTransHook);
          611  +  assert( SQLITE_BEGIN==1 );
          612  +  assert( SQLITE_COMMIT==2 );
          613  +  assert( SQLITE_ROLLBACK==3 );
          614  +
          615  +  pScript = Tcl_DuplicateObj(pDb->pTransHook);
          616  +  Tcl_IncrRefCount(pScript);
          617  +  Tcl_ListObjAppendElement(interp, pScript, Tcl_NewStringObj(azStr[op-1], -1));
          618  +  Tcl_ListObjAppendElement(interp, pScript, Tcl_NewIntObj(iLevel));
          619  +  if( TCL_OK!=Tcl_EvalObjEx(interp, pScript, 0) ){
          620  +    Tcl_BackgroundError(interp);
          621  +  }
          622  +  Tcl_DecrRefCount(pScript);
          623  +}
   599    624   
   600    625   /*
   601    626   ** This procedure handles wal_hook callbacks.
   602    627   */
   603    628   static int DbWalHandler(
   604    629     void *clientData, 
   605    630     sqlite3 *db, 
................................................................................
  1624   1649       }
  1625   1650     }
  1626   1651   
  1627   1652     sqlite3_preupdate_hook(db, (pDb->pPreUpdateHook?DbPreUpdateHandler:0), pDb);
  1628   1653     sqlite3_update_hook(db, (pDb->pUpdateHook?DbUpdateHandler:0), pDb);
  1629   1654     sqlite3_rollback_hook(db, (pDb->pRollbackHook?DbRollbackHandler:0), pDb);
  1630   1655     sqlite3_wal_hook(db, (pDb->pWalHook?DbWalHandler:0), pDb);
         1656  +  sqlite3_transaction_hook(db, (pDb->pTransHook?DbTransHandler:0), pDb);
  1631   1657   }
  1632   1658   
  1633   1659   /*
  1634   1660   ** The "sqlite" command below creates a new Tcl command for each
  1635   1661   ** connection it opens to an SQLite database.  This routine is invoked
  1636   1662   ** whenever one of those connection-specific commands is executed
  1637   1663   ** in Tcl.  For example, if you run Tcl code like this:
................................................................................
  1655   1681       "errorcode",          "eval",              "exists",
  1656   1682       "function",           "incrblob",          "interrupt",
  1657   1683       "last_insert_rowid",  "nullvalue",         "onecolumn",
  1658   1684       "preupdate",
  1659   1685       "profile",            "progress",          "rekey",
  1660   1686       "restore",            "rollback_hook",     "status",
  1661   1687       "timeout",            "total_changes",     "trace",
  1662         -    "transaction",        "unlock_notify",     "update_hook",
         1688  +    "transaction",        "transaction_hook",
         1689  +    "unlock_notify",     "update_hook",
  1663   1690       "version",            "wal_hook",          0
  1664   1691     };
  1665   1692     enum DB_enum {
  1666   1693       DB_AUTHORIZER,        DB_BACKUP,           DB_BUSY,
  1667   1694       DB_CACHE,             DB_CHANGES,          DB_CLOSE,
  1668   1695       DB_COLLATE,           DB_COLLATION_NEEDED, DB_COMMIT_HOOK,
  1669   1696       DB_COMPLETE,          DB_COPY,             DB_ENABLE_LOAD_EXTENSION,
................................................................................
  1670   1697       DB_ERRORCODE,         DB_EVAL,             DB_EXISTS,
  1671   1698       DB_FUNCTION,          DB_INCRBLOB,         DB_INTERRUPT,
  1672   1699       DB_LAST_INSERT_ROWID, DB_NULLVALUE,        DB_ONECOLUMN,
  1673   1700       DB_PREUPDATE,
  1674   1701       DB_PROFILE,           DB_PROGRESS,         DB_REKEY,
  1675   1702       DB_RESTORE,           DB_ROLLBACK_HOOK,    DB_STATUS,
  1676   1703       DB_TIMEOUT,           DB_TOTAL_CHANGES,    DB_TRACE,
  1677         -    DB_TRANSACTION,       DB_UNLOCK_NOTIFY,    DB_UPDATE_HOOK,
         1704  +    DB_TRANSACTION,       DB_TRANSACTION_HOOK,
         1705  +    DB_UNLOCK_NOTIFY,     DB_UPDATE_HOOK,
  1678   1706       DB_VERSION,           DB_WAL_HOOK
  1679   1707     };
  1680   1708     /* don't leave trailing commas on DB_enum, it confuses the AIX xlc compiler */
  1681   1709   
  1682   1710     if( objc<2 ){
  1683   1711       Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ...");
  1684   1712       return TCL_ERROR;
................................................................................
  2910   2938       break;
  2911   2939     }
  2912   2940   
  2913   2941     /*
  2914   2942     **    $db wal_hook ?script?
  2915   2943     **    $db update_hook ?script?
  2916   2944     **    $db rollback_hook ?script?
         2945  +  **    $db transaction_hook ?script?
  2917   2946     */
  2918   2947     case DB_WAL_HOOK: 
  2919   2948     case DB_UPDATE_HOOK: 
  2920         -  case DB_ROLLBACK_HOOK: {
         2949  +  case DB_ROLLBACK_HOOK:
         2950  +  case DB_TRANSACTION_HOOK: {
  2921   2951       sqlite3 *db = pDb->db;
  2922   2952   
  2923   2953       /* set ppHook to point at pUpdateHook or pRollbackHook, depending on 
  2924   2954       ** whether [$db update_hook] or [$db rollback_hook] was invoked.
  2925   2955       */
  2926   2956       Tcl_Obj **ppHook; 
  2927   2957       if( choice==DB_WAL_HOOK ) ppHook = &pDb->pWalHook;
  2928   2958       if( choice==DB_UPDATE_HOOK ) ppHook = &pDb->pUpdateHook;
  2929   2959       if( choice==DB_ROLLBACK_HOOK ) ppHook = &pDb->pRollbackHook;
         2960  +    if( choice==DB_TRANSACTION_HOOK ) ppHook = &pDb->pTransHook;
  2930   2961       if( objc>3 ){
  2931   2962          Tcl_WrongNumArgs(interp, 2, objv, "?SCRIPT?");
  2932   2963          return TCL_ERROR;
  2933   2964       }
  2934   2965   
  2935   2966       DbHookCmd(interp, pDb, (objc==3 ? objv[2] : 0), ppHook);
  2936   2967       break;

Changes to src/vdbe.c.

  2564   2564           ** "transaction savepoint". */
  2565   2565           if( db->autoCommit ){
  2566   2566             db->autoCommit = 0;
  2567   2567             db->isTransactionSavepoint = 1;
  2568   2568           }else{
  2569   2569             db->nSavepoint++;
  2570   2570           }
         2571  +
         2572  +        sqlite3TransactionHook(db, SQLITE_BEGIN, db->nSavepoint);
  2571   2573       
  2572   2574           /* Link the new savepoint into the database handle's list. */
  2573   2575           pNew->pNext = db->pSavepoint;
  2574   2576           db->pSavepoint = pNew;
  2575   2577           pNew->nDeferredCons = db->nDeferredCons;
  2576   2578         }
  2577   2579       }
................................................................................
  2631   2633             }
  2632   2634           }
  2633   2635           if( p1==SAVEPOINT_ROLLBACK && (db->flags&SQLITE_InternChanges)!=0 ){
  2634   2636             sqlite3ExpirePreparedStatements(db);
  2635   2637             sqlite3ResetInternalSchema(db, 0);
  2636   2638             db->flags = (db->flags | SQLITE_InternChanges);
  2637   2639           }
         2640  +
         2641  +        assert( SAVEPOINT_ROLLBACK+1==SQLITE_ROLLBACK );
         2642  +        assert( SAVEPOINT_RELEASE+1==SQLITE_COMMIT );
         2643  +        sqlite3TransactionHook(db, p1+1, iSavepoint+1);
         2644  +        if( p1==SAVEPOINT_ROLLBACK ){
         2645  +          sqlite3TransactionHook(db, SQLITE_BEGIN, iSavepoint+1);
         2646  +        }
  2638   2647         }
  2639   2648     
  2640   2649         /* Regardless of whether this is a RELEASE or ROLLBACK, destroy all 
  2641   2650         ** savepoints nested inside of the savepoint being operated on. */
  2642   2651         while( db->pSavepoint!=pSavepoint ){
  2643   2652           pTmp = db->pSavepoint;
  2644   2653           db->pSavepoint = pTmp->pNext;
................................................................................
  2706   2715       if( iRollback ){
  2707   2716         assert( desiredAutoCommit==1 );
  2708   2717         sqlite3RollbackAll(db);
  2709   2718         db->autoCommit = 1;
  2710   2719       }else if( (rc = sqlite3VdbeCheckFk(p, 1))!=SQLITE_OK ){
  2711   2720         goto vdbe_return;
  2712   2721       }else{
         2722  +      if( desiredAutoCommit==0 ){
         2723  +        sqlite3TransactionHook(db, SQLITE_BEGIN, 0);
         2724  +      }
  2713   2725         db->autoCommit = (u8)desiredAutoCommit;
  2714   2726         if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){
  2715   2727           p->pc = pc;
  2716   2728           db->autoCommit = (u8)(1-desiredAutoCommit);
  2717   2729           p->rc = rc = SQLITE_BUSY;
  2718   2730           goto vdbe_return;
  2719   2731         }
................................................................................
  2792   2804         assert( sqlite3BtreeIsInTrans(pBt) );
  2793   2805         if( p->iStatement==0 ){
  2794   2806           assert( db->nStatement>=0 && db->nSavepoint>=0 );
  2795   2807           db->nStatement++; 
  2796   2808           p->iStatement = db->nSavepoint + db->nStatement;
  2797   2809         }
  2798   2810         rc = sqlite3BtreeBeginStmt(pBt, p->iStatement);
         2811  +      sqlite3TransactionHook(db, SQLITE_BEGIN, p->iStatement);
  2799   2812   
  2800   2813         /* Store the current value of the database handles deferred constraint
  2801   2814         ** counter. If the statement transaction needs to be rolled back,
  2802   2815         ** the value of this counter needs to be restored too.  */
  2803   2816         p->nStmtDefCons = db->nDeferredCons;
  2804   2817       }
  2805   2818     }

Changes to src/vdbeapi.c.

   397    397   #ifndef SQLITE_OMIT_TRACE
   398    398       if( db->xProfile && !db->init.busy ){
   399    399         sqlite3OsCurrentTimeInt64(db->pVfs, &p->startTime);
   400    400       }
   401    401   #endif
   402    402   
   403    403       db->activeVdbeCnt++;
   404         -    if( p->readOnly==0 ) db->writeVdbeCnt++;
          404  +    if( p->readOnly==0 ){ 
          405  +      /* If this statement will open an implicit transaction, invoke the
          406  +      ** transaction-hook here. */
          407  +      if( db->autoCommit && db->writeVdbeCnt==0 ){
          408  +        assert( db->nSavepoint==0 );
          409  +        sqlite3TransactionHook(db, SQLITE_BEGIN, 0);
          410  +      }
          411  +      db->writeVdbeCnt++;
          412  +    }
   405    413       p->pc = 0;
   406    414     }
   407    415   #ifndef SQLITE_OMIT_EXPLAIN
   408    416     if( p->explain ){
   409    417       rc = sqlite3VdbeList(p);
   410    418     }else
   411    419   #endif /* SQLITE_OMIT_EXPLAIN */

Changes to src/vdbeaux.c.

  1840   1840       }
  1841   1841       sqlite3EndBenignMalloc();
  1842   1842       enable_simulated_io_errors();
  1843   1843   
  1844   1844       sqlite3VtabCommit(db);
  1845   1845     }
  1846   1846   #endif
         1847  +
         1848  +  /* If a transaction-hook is configured, invoke it now to report on the
         1849  +  ** successful commit operation. */
         1850  +  if( rc==SQLITE_OK ){
         1851  +    sqlite3TransactionHook(db, SQLITE_COMMIT, 0);
         1852  +  }
  1847   1853   
  1848   1854     return rc;
  1849   1855   }
  1850   1856   
  1851   1857   /* 
  1852   1858   ** This routine checks that the sqlite3.activeVdbeCnt count variable
  1853   1859   ** matches the number of vdbe's in the list sqlite3.pVdbe that are
................................................................................
  1942   1948           }
  1943   1949           if( rc==SQLITE_OK ){
  1944   1950             rc = rc2;
  1945   1951           }
  1946   1952         }
  1947   1953       }
  1948   1954       db->nStatement--;
         1955  +    assert( SAVEPOINT_ROLLBACK+1==SQLITE_ROLLBACK );
         1956  +    assert( SAVEPOINT_RELEASE+1==SQLITE_COMMIT );
         1957  +    sqlite3TransactionHook(db, eOp+1, p->iStatement);
  1949   1958       p->iStatement = 0;
  1950   1959   
  1951   1960       /* If the statement transaction is being rolled back, also restore the 
  1952   1961       ** database handles deferred constraint counter to the value it had when 
  1953   1962       ** the statement transaction was opened.  */
  1954   1963       if( eOp==SAVEPOINT_ROLLBACK ){
  1955   1964         db->nDeferredCons = p->nStmtDefCons;

Changes to test/hook.test.

   612    612   do_execsql_test 7.5.2.0 {
   613    613     CREATE TABLE t8(a, b);
   614    614     INSERT INTO t8 VALUES('one', 'two');
   615    615     INSERT INTO t8 VALUES('three', 'four');
   616    616     ALTER TABLE t8 ADD COLUMN c DEFAULT 'xxx';
   617    617   }
   618    618   
          619  +# At time of writing, these two are broken. They demonstraight that the
          620  +# sqlite3_preupdate_old() method does not handle the case where ALTER TABLE
          621  +# has been used to add a column with a default value other than NULL.
          622  +#
   619    623   do_preupdate_test 7.5.2.1 {
   620    624     DELETE FROM t8 WHERE a = 'one'
   621    625   } {
   622    626     DELETE main t8 1 1   one two xxx
   623    627   }
   624         -
   625    628   do_preupdate_test 7.5.2.2 {
   626    629     UPDATE t8 SET b = 'five'
   627    630   } {
   628    631     UPDATE main t8 2 2   three four xxx
   629    632   }
          633  +
          634  +#----------------------------------------------------------------------------
          635  +# The following tests - hook-8.* - test the transaction hook.
          636  +#
          637  +db close
          638  +forcedelete test.db
          639  +sqlite3 db test.db
          640  +
          641  +proc transaction_hook {op iLevel} {
          642  +  lappend ::transaction_hook $op $iLevel
          643  +}
          644  +db transaction_hook transaction_hook
          645  +
          646  +proc do_transaction_test {tn sql x} {
          647  +  set X [list]
          648  +  foreach elem $x {lappend X $elem}
          649  +
          650  +  uplevel do_test $tn [list "
          651  +    set ::transaction_hook \[list\]
          652  +    catchsql { $sql }
          653  +    set ::transaction_hook
          654  +  "] [list $X]
          655  +}
          656  +
          657  +do_transaction_test 8.1.1 "CREATE TABLE t1(x)" {BEGIN 0 COMMIT 0}
          658  +do_transaction_test 8.1.2 "BEGIN"              {BEGIN 0}
          659  +do_transaction_test 8.1.3 "COMMIT"             {COMMIT 0}
          660  +do_transaction_test 8.1.4 "BEGIN ; ROLLBACK"   {BEGIN 0 ROLLBACK 0}
          661  +
          662  +do_execsql_test 8.2.0 {
          663  +  CREATE TABLE t2(a PRIMARY KEY, b);
          664  +  INSERT INTO t2 VALUES(1, 'one');
          665  +  INSERT INTO t2 VALUES(2, 'two');
          666  +  INSERT INTO t2 VALUES(3, 'three');
          667  +}
          668  +do_transaction_test 8.2.1 {
          669  +  INSERT INTO t2 VALUES(2, 'xxx')
          670  +} {BEGIN 0 ROLLBACK 0}
          671  +
          672  +do_transaction_test 8.2.2 {
          673  +  BEGIN; INSERT INTO t2 SELECT a-2, b FROM t2;
          674  +} {BEGIN 0 BEGIN 1 ROLLBACK 1}
          675  +
          676  +do_transaction_test 8.2.3 {
          677  +  INSERT OR ROLLBACK INTO t2 SELECT a-2, b FROM t2;
          678  +} {ROLLBACK 0}
          679  +
          680  +do_transaction_test 8.2.4 {
          681  +  BEGIN; INSERT INTO t2 SELECT a+3, b FROM t2;
          682  +} {BEGIN 0 BEGIN 1 COMMIT 1}
          683  +
          684  +do_transaction_test 8.2.5 "COMMIT" {COMMIT 0}
          685  +
          686  +do_transaction_test 8.3.1 {SELECT * FROM t2} {}
          687  +
          688  +do_transaction_test 8.4.1 {
          689  +  SAVEPOINT top;
          690  +  RELEASE top;
          691  +} {BEGIN 0 COMMIT 0}
          692  +
          693  +do_transaction_test 8.4.2 {
          694  +  SAVEPOINT top;
          695  +  ROLLBACK TO top;
          696  +  RELEASE top;
          697  +} {BEGIN 0 ROLLBACK 0 BEGIN 0 COMMIT 0}
          698  +
          699  +do_transaction_test 8.4.3 {
          700  +  SAVEPOINT zero;
          701  +    SAVEPOINT one;
          702  +      SAVEPOINT two;
          703  +        SAVEPOINT three;
          704  +  ROLLBACK TO zero;
          705  +    SAVEPOINT one;
          706  +      SAVEPOINT two;
          707  +        SAVEPOINT three;
          708  +    ROLLBACK TO one;
          709  +      SAVEPOINT two;
          710  +  RELEASE zero;
          711  +
          712  +  SAVEPOINT zero;
          713  +    SAVEPOINT one;
          714  +      SAVEPOINT two;
          715  +      RELEASE two;
          716  +      SAVEPOINT two;
          717  +        SAVEPOINT three;
          718  +    RELEASE one;
          719  +  ROLLBACK TO zero;
          720  +  RELEASE zero;
          721  +} {
          722  +  BEGIN 0 BEGIN 1 BEGIN 2 BEGIN 3 ROLLBACK 0 BEGIN 0 
          723  +  BEGIN 1 BEGIN 2 BEGIN 3 ROLLBACK 1 BEGIN 1 BEGIN 2 
          724  +  COMMIT 0
          725  +
          726  +  BEGIN 0 BEGIN 1 BEGIN 2 COMMIT 2 BEGIN 2 BEGIN 3 COMMIT 1 
          727  +  ROLLBACK 0 BEGIN 0 COMMIT 0
          728  +}
          729  +
          730  +do_transaction_test 8.4.4 {
          731  +  BEGIN;
          732  +    SAVEPOINT zero;
          733  +      SAVEPOINT one;
          734  +        SAVEPOINT two;
          735  +          SAVEPOINT three;
          736  +    ROLLBACK TO zero;
          737  +      SAVEPOINT one;
          738  +        SAVEPOINT two;
          739  +          SAVEPOINT three;
          740  +      ROLLBACK TO one;
          741  +        SAVEPOINT two;
          742  +    RELEASE zero;
          743  +  
          744  +    SAVEPOINT zero;
          745  +      SAVEPOINT one;
          746  +        SAVEPOINT two;
          747  +        RELEASE two;
          748  +        SAVEPOINT two;
          749  +          SAVEPOINT three;
          750  +      RELEASE one;
          751  +    ROLLBACK TO zero;
          752  +    RELEASE zero;
          753  +  COMMIT;
          754  +} {
          755  +  BEGIN 0
          756  +  BEGIN 1 BEGIN 2 BEGIN 3 BEGIN 4 ROLLBACK 1 BEGIN 1 
          757  +  BEGIN 2 BEGIN 3 BEGIN 4 ROLLBACK 2 BEGIN 2 BEGIN 3 
          758  +  COMMIT 1
          759  +
          760  +  BEGIN 1 BEGIN 2 BEGIN 3 COMMIT 3 BEGIN 3 BEGIN 4 COMMIT 2 
          761  +  ROLLBACK 1 BEGIN 1 COMMIT 1
          762  +  COMMIT 0
          763  +}
   630    764   
   631    765   finish_test
          766  +

Changes to test/tclsqlite.test.

    31     31     set v [catch {sqlite3 bogus} msg]
    32     32     regsub {really_sqlite3} $msg {sqlite3} msg
    33     33     lappend v $msg
    34     34   } [list 1 "wrong # args: should be \"$r\""]
    35     35   do_test tcl-1.2 {
    36     36     set v [catch {db bogus} msg]
    37     37     lappend v $msg
    38         -} {1 {bad option "bogus": must be authorizer, backup, busy, cache, changes, close, collate, collation_needed, commit_hook, complete, copy, enable_load_extension, errorcode, eval, exists, function, incrblob, interrupt, last_insert_rowid, nullvalue, onecolumn, preupdate, profile, progress, rekey, restore, rollback_hook, status, timeout, total_changes, trace, transaction, unlock_notify, update_hook, version, or wal_hook}}
           38  +} {1 {bad option "bogus": must be authorizer, backup, busy, cache, changes, close, collate, collation_needed, commit_hook, complete, copy, enable_load_extension, errorcode, eval, exists, function, incrblob, interrupt, last_insert_rowid, nullvalue, onecolumn, preupdate, profile, progress, rekey, restore, rollback_hook, status, timeout, total_changes, trace, transaction, transaction_hook, unlock_notify, update_hook, version, or wal_hook}}
    39     39   do_test tcl-1.2.1 {
    40     40     set v [catch {db cache bogus} msg]
    41     41     lappend v $msg
    42     42   } {1 {bad option "bogus": must be flush or size}}
    43     43   do_test tcl-1.2.2 {
    44     44     set v [catch {db cache} msg]
    45     45     lappend v $msg