/ Check-in [9bda6014]
Login

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

Overview
Comment:Add the sqlite3_log_hook() interface for scheduling checkpoints.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | wal
Files: files | file ages | folders
SHA1: 9bda601455705475075e33bfa85687bce34b15ff
User & Date: dan 2010-04-19 18:03:52
Context
2010-04-20
18:53
Use the read and write version fields of the database header to mark a database as operating in wal-mode. check-in: 96bef18c user: dan tags: wal
2010-04-19
18:03
Add the sqlite3_log_hook() interface for scheduling checkpoints. check-in: 9bda6014 user: dan tags: wal
2010-04-17
18:50
Add some comments regarding file-locks to log.c. check-in: 9d51c3b7 user: dan tags: wal
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/log.c.

   198    198   ** logLockRegion() function.
   199    199   */
   200    200   #define LOG_REGION_A 0x01
   201    201   #define LOG_REGION_B 0x02
   202    202   #define LOG_REGION_C 0x04
   203    203   #define LOG_REGION_D 0x08
   204    204   
          205  +/*
          206  +** Values for the third parameter to logLockRegion().
          207  +*/
          208  +#define LOG_UNLOCK  0             /* Unlock a range of bytes */
          209  +#define LOG_RDLOCK  1             /* Put a SHARED lock on a range of bytes */
          210  +#define LOG_WRLOCK  2             /* Put an EXCLUSIVE lock on a byte-range */
          211  +#define LOG_WRLOCKW 3             /* Block on EXCLUSIVE lock on a byte-range */
          212  +
   205    213   /*
   206    214   ** A single instance of this structure is allocated as part of each 
   207    215   ** connection to a database log. All structures associated with the 
   208    216   ** same log file are linked together into a list using LogLock.pNext
   209    217   ** starting at LogSummary.pLock.
   210    218   **
   211    219   ** The mLock field of the structure describes the locks (if any) 
................................................................................
   221    229   
   222    230   struct Log {
   223    231     LogSummary *pSummary;           /* Log file summary data */
   224    232     sqlite3_vfs *pVfs;              /* The VFS used to create pFd */
   225    233     sqlite3_file *pFd;              /* File handle for log file */
   226    234     int isLocked;                   /* Non-zero if a snapshot is held open */
   227    235     int isWriteLocked;              /* True if this is the writer connection */
          236  +  u32 iCallback;                  /* Value to pass to log callback (or 0) */
   228    237     LogSummaryHdr hdr;              /* Log summary header for current snapshot */
   229    238     LogLock lock;                   /* Lock held by this connection (if any) */
   230    239   };
   231    240   
   232    241   
   233    242   /*
   234    243   ** This structure is used to implement an iterator that iterates through
................................................................................
   643    652   
   644    653   finished:
   645    654     logSummaryWriteHdr(pSummary, &hdr);
   646    655     return rc;
   647    656   }
   648    657   
   649    658   /*
   650         -** Values for the third parameter to logLockRegion().
          659  +** Place, modify or remove a lock on the log-summary file associated 
          660  +** with pSummary.
   651    661   */
   652         -#define LOG_UNLOCK  0
   653         -#define LOG_RDLOCK  1
   654         -#define LOG_WRLOCK  2
   655         -#define LOG_WRLOCKW 3
   656         -
   657         -static int logLockFd(LogSummary *pSummary, int iStart, int nByte, int op){
          662  +static int logLockFd(
          663  +  LogSummary *pSummary,           /* The log-summary object to lock */
          664  +  int iStart,                     /* First byte to lock */
          665  +  int nByte,                      /* Number of bytes to lock */
          666  +  int op                          /* LOG_UNLOCK, RDLOCK, WRLOCK or WRLOCKW */
          667  +){
   658    668     int aType[4] = { 
   659         -    F_UNLCK,                    /* LOG_UNLOCK */
   660         -    F_RDLCK,                    /* LOG_RDLOCK */
   661         -    F_WRLCK,                    /* LOG_WRLOCK */
   662         -    F_WRLCK                     /* LOG_WRLOCKW */
          669  +    F_UNLCK,                      /* LOG_UNLOCK */
          670  +    F_RDLCK,                      /* LOG_RDLOCK */
          671  +    F_WRLCK,                      /* LOG_WRLOCK */
          672  +    F_WRLCK                       /* LOG_WRLOCKW */
   663    673     };
   664    674     int aOp[4] = { 
   665         -    F_SETLK,                    /* LOG_UNLOCK */
   666         -    F_SETLK,                    /* LOG_RDLOCK */
   667         -    F_SETLK,                    /* LOG_WRLOCK */
   668         -    F_SETLKW                    /* LOG_WRLOCKW */
          675  +    F_SETLK,                      /* LOG_UNLOCK */
          676  +    F_SETLK,                      /* LOG_RDLOCK */
          677  +    F_SETLK,                      /* LOG_WRLOCK */
          678  +    F_SETLKW                      /* LOG_WRLOCKW */
   669    679     };
   670         -
   671         -  struct flock f;               /* Locking operation */
   672         -  int rc;                       /* Value returned by fcntl() */
          680  +  struct flock f;                 /* Locking operation */
          681  +  int rc;                         /* Value returned by fcntl() */
   673    682   
   674    683     assert( ArraySize(aType)==ArraySize(aOp) );
   675    684     assert( op>=0 && op<ArraySize(aType) );
   676    685   
   677    686     memset(&f, 0, sizeof(f));
   678    687     f.l_type = aType[op];
   679    688     f.l_whence = SEEK_SET;
................................................................................
   812    821     }
   813    822   
   814    823     pLog->lock.mLock = mNew;
   815    824     sqlite3_mutex_leave(pSummary->mutex);
   816    825     return SQLITE_OK;
   817    826   }
   818    827   
          828  +/*
          829  +** Lock the DMH region, either with an EXCLUSIVE or SHARED lock. This
          830  +** function is never called with LOG_UNLOCK - the only way the DMH region
          831  +** is every completely unlocked is by by closing the file descriptor.
          832  +*/
   819    833   static int logLockDMH(LogSummary *pSummary, int eLock){
          834  +  assert( sqlite3_mutex_held(pSummary->mutex) );
   820    835     assert( eLock==LOG_RDLOCK || eLock==LOG_WRLOCK );
   821    836     return logLockFd(pSummary, LOG_LOCK_DMH, 1, eLock);
   822    837   }
   823    838   
          839  +/*
          840  +** Lock (or unlock) the MUTEX region. It is always locked using an
          841  +** EXCLUSIVE, blocking lock.
          842  +*/
   824    843   static int logLockMutex(LogSummary *pSummary, int eLock){
          844  +  assert( sqlite3_mutex_held(pSummary->mutex) );
   825    845     assert( eLock==LOG_WRLOCKW || eLock==LOG_UNLOCK );
   826    846     logLockFd(pSummary, LOG_LOCK_MUTEX, 1, eLock);
   827    847     return SQLITE_OK;
   828    848   }
   829         -
   830         -
   831    849   
   832    850   /*
   833    851   ** This function intializes the connection to the log-summary identified
   834    852   ** by struct pSummary.
   835    853   */
   836    854   static int logSummaryInit(
   837    855     LogSummary *pSummary,           /* Log summary object to initialize */
................................................................................
   876    894     */
   877    895     rc = logLockDMH(pSummary, LOG_WRLOCK);
   878    896     if( rc==SQLITE_OK ){
   879    897       memset(pSummary->aData, 0, (LOGSUMMARY_HDR_NFIELD+2)*sizeof(u32) );
   880    898     }
   881    899     rc = logLockDMH(pSummary, LOG_RDLOCK);
   882    900     if( rc!=SQLITE_OK ){
   883         -    return SQLITE_IOERR;
          901  +    rc = SQLITE_IOERR;
   884    902     }
   885    903   
   886    904    out:
   887    905     logLockMutex(pSummary, LOG_UNLOCK);
   888    906     return rc;
   889    907   }
   890    908   
................................................................................
  1457   1475     return SQLITE_OK;
  1458   1476   }
  1459   1477   
  1460   1478   
  1461   1479   /* 
  1462   1480   ** Set *pPgno to the size of the database file (or zero, if unknown).
  1463   1481   */
  1464         -void sqlite3LogMaxpgno(Log *pLog, Pgno *pPgno){
         1482  +void sqlite3LogDbsize(Log *pLog, Pgno *pPgno){
  1465   1483     assert( pLog->isLocked );
  1466   1484     *pPgno = pLog->hdr.nPage;
  1467   1485   }
  1468   1486   
  1469   1487   /* 
  1470   1488   ** This function returns SQLITE_OK if the caller may write to the database.
  1471   1489   ** Otherwise, if the caller is operating on a snapshot that has already
................................................................................
  1642   1660     pLog->hdr.iCheck1 = aCksum[0];
  1643   1661     pLog->hdr.iCheck2 = aCksum[1];
  1644   1662   
  1645   1663     /* If this is a commit, update the log-summary header too. */
  1646   1664     if( isCommit && SQLITE_OK==(rc = logEnterMutex(pLog)) ){
  1647   1665       logSummaryWriteHdr(pLog->pSummary, &pLog->hdr);
  1648   1666       logLeaveMutex(pLog);
         1667  +    pLog->iCallback = iFrame;
  1649   1668     }
  1650   1669   
  1651         -  return SQLITE_OK;
         1670  +  return rc;
  1652   1671   }
  1653   1672   
  1654   1673   /* 
  1655   1674   ** Checkpoint the database:
  1656   1675   **
  1657   1676   **   1. Wait for an EXCLUSIVE lock on regions B and C.
  1658   1677   **   2. Wait for an EXCLUSIVE lock on region A.
................................................................................
  1693   1712       rc = logCheckpoint(pLog, pFd, sync_flags, zBuf);
  1694   1713     }
  1695   1714   
  1696   1715     /* Release the locks. */
  1697   1716     logLockRegion(pLog, LOG_REGION_A|LOG_REGION_B|LOG_REGION_C, LOG_UNLOCK);
  1698   1717     return rc;
  1699   1718   }
         1719  +
         1720  +int sqlite3LogCallback(Log *pLog){
         1721  +  u32 ret = 0;
         1722  +  if( pLog ){
         1723  +    ret = pLog->iCallback;
         1724  +    pLog->iCallback = 0;
         1725  +  }
         1726  +  return (int)ret;
         1727  +}
  1700   1728   

Changes to src/log.h.

    28     28   
    29     29   /* Used by readers to open (lock) and close (unlock) a snapshot. */
    30     30   int sqlite3LogOpenSnapshot(Log *pLog, int *);
    31     31   void sqlite3LogCloseSnapshot(Log *pLog);
    32     32   
    33     33   /* Read a page from the log, if it is present. */
    34     34   int sqlite3LogRead(Log *pLog, Pgno pgno, int *pInLog, u8 *pOut);
    35         -void sqlite3LogMaxpgno(Log *pLog, Pgno *pPgno);
           35  +void sqlite3LogDbsize(Log *pLog, Pgno *pPgno);
    36     36   
    37     37   /* Obtain or release the WRITER lock. */
    38     38   int sqlite3LogWriteLock(Log *pLog, int op);
    39     39   
    40     40   /* Write a frame or frames to the log. */
    41     41   int sqlite3LogFrames(Log *pLog, int, PgHdr *, Pgno, int, int);
    42     42   
................................................................................
    45     45     Log *pLog,                      /* Log connection */
    46     46     sqlite3_file *pFd,              /* File descriptor open on db file */
    47     47     int sync_flags,                 /* Flags to sync db file with (or 0) */
    48     48     u8 *zBuf,                       /* Temporary buffer to use */
    49     49     int (*xBusyHandler)(void *),    /* Pointer to busy-handler function */
    50     50     void *pBusyHandlerArg           /* Argument to pass to xBusyHandler */
    51     51   );
           52  +
           53  +/* Return the value to pass to a log callback. Or 0 for no callback. */
           54  +int sqlite3LogCallback(Log *pLog);
    52     55   
    53     56   #endif /* _LOG_H_ */

Changes to src/main.c.

  1181   1181     sqlite3_mutex_enter(db->mutex);
  1182   1182     pRet = db->pRollbackArg;
  1183   1183     db->xRollbackCallback = xCallback;
  1184   1184     db->pRollbackArg = pArg;
  1185   1185     sqlite3_mutex_leave(db->mutex);
  1186   1186     return pRet;
  1187   1187   }
         1188  +
         1189  +/*
         1190  +** Register a callback to be invoked each time a transaction is written
         1191  +** into the write-ahead-log by this database connection.
         1192  +*/
         1193  +void *sqlite3_log_hook(
         1194  +  sqlite3 *db,                    /* Attach the hook to this db handle */
         1195  +  int(*xCallback)(void *, sqlite3*, const char*, int),
         1196  +  void *pArg                      /* First argument passed to xCallback() */
         1197  +){
         1198  +  void *pRet;
         1199  +  sqlite3_mutex_enter(db->mutex);
         1200  +  pRet = db->pLogArg;
         1201  +  db->xLogCallback = xCallback;
         1202  +  db->pLogArg = pArg;
         1203  +  sqlite3_mutex_leave(db->mutex);
         1204  +  return pRet;
         1205  +}
  1188   1206   
  1189   1207   /*
  1190   1208   ** This function returns true if main-memory should be used instead of
  1191   1209   ** a temporary file for transient pager files and statement journals.
  1192   1210   ** The value returned depends on the value of db->temp_store (runtime
  1193   1211   ** parameter) and the compile time value of SQLITE_TEMP_STORE. The
  1194   1212   ** following table describes the relationship between these two values

Changes to src/pager.c.

  2682   2682     if( pPager->dbSizeValid ){
  2683   2683       nPage = pPager->dbSize;
  2684   2684     }else{
  2685   2685       int rc;                 /* Error returned by OsFileSize() */
  2686   2686       i64 n = 0;              /* File size in bytes returned by OsFileSize() */
  2687   2687   
  2688   2688       if( pagerUseLog(pPager) ){
  2689         -      sqlite3LogMaxpgno(pPager->pLog, &nPage);
         2689  +      sqlite3LogDbsize(pPager->pLog, &nPage);
  2690   2690       }
  2691   2691   
  2692   2692       if( nPage==0 ){
  2693   2693         assert( isOpen(pPager->fd) || pPager->tempFile );
  2694   2694         if( isOpen(pPager->fd) ){
  2695   2695           if( SQLITE_OK!=(rc = sqlite3OsFileSize(pPager->fd, &n)) ){
  2696   2696             pager_error(pPager, rc);
................................................................................
  5704   5704       rc = sqlite3LogCheckpoint(pPager->pLog, pPager->fd, 
  5705   5705           (pPager->noSync ? 0 : pPager->sync_flags),
  5706   5706           zBuf, pPager->xBusyHandler, pPager->pBusyHandlerArg
  5707   5707       );
  5708   5708     }
  5709   5709     return rc;
  5710   5710   }
         5711  +
         5712  +int sqlite3PagerLogCallback(Pager *pPager){
         5713  +  return sqlite3LogCallback(pPager->pLog);
         5714  +}
  5711   5715   
  5712   5716   #endif /* SQLITE_OMIT_DISKIO */

Changes to src/pager.h.

   129    129   int sqlite3PagerCommitPhaseOne(Pager*,const char *zMaster, int);
   130    130   int sqlite3PagerSync(Pager *pPager);
   131    131   int sqlite3PagerCommitPhaseTwo(Pager*);
   132    132   int sqlite3PagerRollback(Pager*);
   133    133   int sqlite3PagerOpenSavepoint(Pager *pPager, int n);
   134    134   int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint);
   135    135   int sqlite3PagerSharedLock(Pager *pPager);
          136  +
   136    137   int sqlite3PagerCheckpoint(Pager *pPager);
          138  +int sqlite3PagerLogCallback(Pager *pPager);
   137    139   
   138    140   /* Functions used to query pager state and configuration. */
   139    141   u8 sqlite3PagerIsreadonly(Pager*);
   140    142   int sqlite3PagerRefcount(Pager*);
   141    143   int sqlite3PagerMemUsed(Pager*);
   142    144   const char *sqlite3PagerFilename(Pager*);
   143    145   const sqlite3_vfs *sqlite3PagerVfs(Pager*);

Changes to src/sqlite.h.in.

  5721   5721   ** To avoid deadlocks and other threading problems, the sqlite3_log() routine
  5722   5722   ** will not use dynamically allocated memory.  The log message is stored in
  5723   5723   ** a fixed-length buffer on the stack.  If the log message is longer than
  5724   5724   ** a few hundred characters, it will be truncated to the length of the
  5725   5725   ** buffer.
  5726   5726   */
  5727   5727   void sqlite3_log(int iErrCode, const char *zFormat, ...);
         5728  +
         5729  +/*
         5730  +** Experimental WAL callback interface.
         5731  +**
         5732  +** The [sqlite3_log_hook()] function is used to register a callback that
         5733  +** will be invoked each time a database connection commits data to a
         5734  +** write-ahead-log (i.e. whenever a transaction is committed in
         5735  +** journal_mode=WAL mode). 
         5736  +**
         5737  +** The callback is invoked by SQLite after the commit has taken place and 
         5738  +** the associated write-lock on the database released, so the implementation 
         5739  +** may read, write or checkpoint the database as required.
         5740  +**
         5741  +** The first parameter passed to the callback function when it is invoked
         5742  +** is a copy of the third parameter passed to sqlite3_log_hook() when
         5743  +** registering the callback. The second is a copy of the database handle.
         5744  +** The third parameter is the name of the database that was written to -
         5745  +** either "main" or the name of an ATTACHed database. The fourth parameter
         5746  +** is the number of pages currently in the log file, including those that
         5747  +** were just committed.
         5748  +**
         5749  +** If an invocation of the callback function returns non-zero, then a
         5750  +** checkpoint is automatically run on the database. If zero is returned,
         5751  +** no special action is taken.
         5752  +**
         5753  +** A single database handle may have at most a single log callback 
         5754  +** registered at one time. Calling [sqlite3_log_hook()] replaces any
         5755  +** previously registered log callback. 
         5756  +*/
         5757  +void *sqlite3_log_hook(
         5758  +  sqlite3*, 
         5759  +  int(*)(void *,sqlite3*,const char*,int),
         5760  +  void*
         5761  +);
  5728   5762   
  5729   5763   /*
  5730   5764   ** Undo the hack that converts floating point types to integer for
  5731   5765   ** builds on processors without floating point support.
  5732   5766   */
  5733   5767   #ifdef SQLITE_OMIT_FLOATING_POINT
  5734   5768   # undef double
  5735   5769   #endif
  5736   5770   
  5737   5771   #ifdef __cplusplus
  5738   5772   }  /* End of the 'extern "C"' block */
  5739   5773   #endif
  5740   5774   #endif

Changes to src/sqliteInt.h.

   819    819     void *pProfileArg;                        /* Argument to profile function */
   820    820     void *pCommitArg;                 /* Argument to xCommitCallback() */   
   821    821     int (*xCommitCallback)(void*);    /* Invoked at every commit. */
   822    822     void *pRollbackArg;               /* Argument to xRollbackCallback() */   
   823    823     void (*xRollbackCallback)(void*); /* Invoked at every commit. */
   824    824     void *pUpdateArg;
   825    825     void (*xUpdateCallback)(void*,int, const char*,const char*,sqlite_int64);
          826  +  int (*xLogCallback)(void *, sqlite3 *, const char *, u32);
          827  +  void *pLogArg;
   826    828     void(*xCollNeeded)(void*,sqlite3*,int eTextRep,const char*);
   827    829     void(*xCollNeeded16)(void*,sqlite3*,int eTextRep,const void*);
   828    830     void *pCollNeededArg;
   829    831     sqlite3_value *pErr;          /* Most recent error message */
   830    832     char *zErrMsg;                /* Most recent error message (UTF-8 encoded) */
   831    833     char *zErrMsg16;              /* Most recent error message (UTF-16 encoded) */
   832    834     union {

Changes to src/tclsqlite.c.

   119    119     char *zProgress;           /* The progress callback routine */
   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 *pRollbackHook;    /* Rollback hook script (if any) */
          126  +  Tcl_Obj *pLogHook;         /* WAL hook script (if any) */
   126    127     Tcl_Obj *pUnlockNotify;    /* Unlock notify script (if any) */
   127    128     SqlCollate *pCollate;      /* List of SQL collation functions */
   128    129     int rc;                    /* Return code of most recent sqlite3_exec() */
   129    130     Tcl_Obj *pCollateNeeded;   /* Collation needed script */
   130    131     SqlPreparedStmt *stmtList; /* List of prepared statements*/
   131    132     SqlPreparedStmt *stmtLast; /* Last statement in the list */
   132    133     int maxStmt;               /* The next maximum number of stmtList */
................................................................................
   481    482     }
   482    483     if( pDb->pUpdateHook ){
   483    484       Tcl_DecrRefCount(pDb->pUpdateHook);
   484    485     }
   485    486     if( pDb->pRollbackHook ){
   486    487       Tcl_DecrRefCount(pDb->pRollbackHook);
   487    488     }
          489  +  if( pDb->pLogHook ){
          490  +    Tcl_DecrRefCount(pDb->pLogHook);
          491  +  }
   488    492     if( pDb->pCollateNeeded ){
   489    493       Tcl_DecrRefCount(pDb->pCollateNeeded);
   490    494     }
   491    495     Tcl_Free((char*)pDb);
   492    496   }
   493    497   
   494    498   /*
................................................................................
   584    588   static void DbRollbackHandler(void *clientData){
   585    589     SqliteDb *pDb = (SqliteDb*)clientData;
   586    590     assert(pDb->pRollbackHook);
   587    591     if( TCL_OK!=Tcl_EvalObjEx(pDb->interp, pDb->pRollbackHook, 0) ){
   588    592       Tcl_BackgroundError(pDb->interp);
   589    593     }
   590    594   }
          595  +
          596  +static int DbLogHandler(
          597  +  void *clientData, 
          598  +  sqlite3 *db, 
          599  +  const char *zDb, 
          600  +  int nEntry
          601  +){
          602  +  int ret = 0;
          603  +  Tcl_Obj *p;
          604  +  SqliteDb *pDb = (SqliteDb*)clientData;
          605  +  Tcl_Interp *interp = pDb->interp;
          606  +  assert(pDb->pLogHook);
          607  +
          608  +  p = Tcl_DuplicateObj(pDb->pLogHook);
          609  +  Tcl_IncrRefCount(p);
          610  +  Tcl_ListObjAppendElement(interp, p, Tcl_NewStringObj(zDb, -1));
          611  +  Tcl_ListObjAppendElement(interp, p, Tcl_NewIntObj(nEntry));
          612  +  if( TCL_OK!=Tcl_EvalObjEx(interp, p, 0) 
          613  +   || TCL_OK!=Tcl_GetIntFromObj(interp, Tcl_GetObjResult(interp), &ret)
          614  +  ){
          615  +    Tcl_BackgroundError(interp);
          616  +  }
          617  +  Tcl_DecrRefCount(p);
          618  +
          619  +  return ret;
          620  +}
   591    621   
   592    622   #if defined(SQLITE_TEST) && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
   593    623   static void setTestUnlockNotifyVars(Tcl_Interp *interp, int iArg, int nArg){
   594    624     char zBuf[64];
   595    625     sprintf(zBuf, "%d", iArg);
   596    626     Tcl_SetVar(interp, "sqlite_unlock_notify_arg", zBuf, TCL_GLOBAL_ONLY);
   597    627     sprintf(zBuf, "%d", nArg);
................................................................................
  1536   1566     static const char *DB_strs[] = {
  1537   1567       "authorizer",         "backup",            "busy",
  1538   1568       "cache",              "changes",           "close",
  1539   1569       "collate",            "collation_needed",  "commit_hook",
  1540   1570       "complete",           "copy",              "enable_load_extension",
  1541   1571       "errorcode",          "eval",              "exists",
  1542   1572       "function",           "incrblob",          "interrupt",
  1543         -    "last_insert_rowid",  "nullvalue",         "onecolumn",
  1544         -    "profile",            "progress",          "rekey",
  1545         -    "restore",            "rollback_hook",     "status",
  1546         -    "timeout",            "total_changes",     "trace",
  1547         -    "transaction",        "unlock_notify",     "update_hook",
  1548         -    "version",            0                    
         1573  +    "last_insert_rowid",  "log_hook",          "nullvalue",
         1574  +    "onecolumn",          "profile",           "progress",
         1575  +    "rekey",              "restore",           "rollback_hook",     
         1576  +    "status",             "timeout",           "total_changes",     
         1577  +    "trace",              "transaction",       "unlock_notify",     
         1578  +    "update_hook",        "version",            0                    
  1549   1579     };
  1550   1580     enum DB_enum {
  1551   1581       DB_AUTHORIZER,        DB_BACKUP,           DB_BUSY,
  1552   1582       DB_CACHE,             DB_CHANGES,          DB_CLOSE,
  1553   1583       DB_COLLATE,           DB_COLLATION_NEEDED, DB_COMMIT_HOOK,
  1554   1584       DB_COMPLETE,          DB_COPY,             DB_ENABLE_LOAD_EXTENSION,
  1555   1585       DB_ERRORCODE,         DB_EVAL,             DB_EXISTS,
  1556   1586       DB_FUNCTION,          DB_INCRBLOB,         DB_INTERRUPT,
  1557         -    DB_LAST_INSERT_ROWID, DB_NULLVALUE,        DB_ONECOLUMN,
  1558         -    DB_PROFILE,           DB_PROGRESS,         DB_REKEY,
  1559         -    DB_RESTORE,           DB_ROLLBACK_HOOK,    DB_STATUS,
  1560         -    DB_TIMEOUT,           DB_TOTAL_CHANGES,    DB_TRACE,
  1561         -    DB_TRANSACTION,       DB_UNLOCK_NOTIFY,    DB_UPDATE_HOOK,
  1562         -    DB_VERSION,
         1587  +    DB_LAST_INSERT_ROWID, DB_LOG_HOOK,         DB_NULLVALUE,
         1588  +    DB_ONECOLUMN,         DB_PROFILE,          DB_PROGRESS,
         1589  +    DB_REKEY,             DB_RESTORE,          DB_ROLLBACK_HOOK,    
         1590  +    DB_STATUS,            DB_TIMEOUT,          DB_TOTAL_CHANGES,    
         1591  +    DB_TRACE,             DB_TRANSACTION,      DB_UNLOCK_NOTIFY,    
         1592  +    DB_UPDATE_HOOK,       DB_VERSION
  1563   1593     };
  1564   1594     /* don't leave trailing commas on DB_enum, it confuses the AIX xlc compiler */
  1565   1595   
  1566   1596     if( objc<2 ){
  1567   1597       Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ...");
  1568   1598       return TCL_ERROR;
  1569   1599     }
................................................................................
  2726   2756         }
  2727   2757       }
  2728   2758   #endif
  2729   2759       break;
  2730   2760     }
  2731   2761   
  2732   2762     /*
         2763  +  **    $db log_hook ?script?
  2733   2764     **    $db update_hook ?script?
  2734   2765     **    $db rollback_hook ?script?
  2735   2766     */
         2767  +  case DB_LOG_HOOK: 
  2736   2768     case DB_UPDATE_HOOK: 
  2737   2769     case DB_ROLLBACK_HOOK: {
  2738   2770   
  2739   2771       /* set ppHook to point at pUpdateHook or pRollbackHook, depending on 
  2740   2772       ** whether [$db update_hook] or [$db rollback_hook] was invoked.
  2741   2773       */
  2742   2774       Tcl_Obj **ppHook; 
  2743   2775       if( choice==DB_UPDATE_HOOK ){
  2744   2776         ppHook = &pDb->pUpdateHook;
         2777  +    }else if( choice==DB_LOG_HOOK ){
         2778  +      ppHook = &pDb->pLogHook;
  2745   2779       }else{
  2746   2780         ppHook = &pDb->pRollbackHook;
  2747   2781       }
  2748   2782   
  2749   2783       if( objc!=2 && objc!=3 ){
  2750   2784          Tcl_WrongNumArgs(interp, 2, objv, "?SCRIPT?");
  2751   2785          return TCL_ERROR;
................................................................................
  2763   2797           *ppHook = objv[2];
  2764   2798           Tcl_IncrRefCount(*ppHook);
  2765   2799         }
  2766   2800       }
  2767   2801   
  2768   2802       sqlite3_update_hook(pDb->db, (pDb->pUpdateHook?DbUpdateHandler:0), pDb);
  2769   2803       sqlite3_rollback_hook(pDb->db,(pDb->pRollbackHook?DbRollbackHandler:0),pDb);
         2804  +    sqlite3_log_hook(pDb->db,(pDb->pLogHook?DbLogHandler:0),pDb);
  2770   2805   
  2771   2806       break;
  2772   2807     }
  2773   2808   
  2774   2809     /*    $db version
  2775   2810     **
  2776   2811     ** Return the version string for this database.

Changes to src/vdbeapi.c.

   301    301   /* An SQLITE_NOMEM error. */
   302    302   void sqlite3_result_error_nomem(sqlite3_context *pCtx){
   303    303     assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
   304    304     sqlite3VdbeMemSetNull(&pCtx->s);
   305    305     pCtx->isError = SQLITE_NOMEM;
   306    306     pCtx->s.db->mallocFailed = 1;
   307    307   }
          308  +
          309  +static int doLogCallbacks(sqlite3 *db){
          310  +  int i;
          311  +  int rc = SQLITE_OK;
          312  +  for(i=0; i<db->nDb; i++){
          313  +    Btree *pBt = db->aDb[i].pBt;
          314  +    if( pBt ){
          315  +      int nEntry = sqlite3PagerLogCallback(sqlite3BtreePager(pBt));
          316  +      if( db->xLogCallback && nEntry>0 && rc==SQLITE_OK
          317  +       && db->xLogCallback(db->pLogArg, db, db->aDb[i].zName, nEntry)
          318  +      ){
          319  +        rc = sqlite3PagerCheckpoint(sqlite3BtreePager(pBt));
          320  +      }
          321  +    }
          322  +  }
          323  +  return rc;
          324  +}
   308    325   
   309    326   /*
   310    327   ** Execute the statement pStmt, either until a row of data is ready, the
   311    328   ** statement is completely executed or an error occurs.
   312    329   **
   313    330   ** This routine implements the bulk of the logic behind the sqlite_step()
   314    331   ** API.  The only thing omitted is the automatic recompile if a 
................................................................................
   382    399   
   383    400       sqlite3OsCurrentTime(db->pVfs, &rNow);
   384    401       elapseTime = (u64)((rNow - (int)rNow)*3600.0*24.0*1000000000.0);
   385    402       elapseTime -= p->startTime;
   386    403       db->xProfile(db->pProfileArg, p->zSql, elapseTime);
   387    404     }
   388    405   #endif
          406  +
          407  +  if( rc==SQLITE_DONE ){
          408  +    assert( p->rc==SQLITE_OK );
          409  +    p->rc = doLogCallbacks(db);
          410  +    if( p->rc!=SQLITE_OK ){
          411  +      rc = SQLITE_ERROR;
          412  +    }
          413  +  }
   389    414   
   390    415     db->errCode = rc;
   391    416     if( SQLITE_NOMEM==sqlite3ApiExit(p->db, p->rc) ){
   392    417       p->rc = SQLITE_NOMEM;
   393    418     }
   394    419   end_of_step:
   395    420     /* At this point local variable rc holds the value that should be 

Changes to src/vdbeaux.c.

  1647   1647   
  1648   1648     /* This loop determines (a) if the commit hook should be invoked and
  1649   1649     ** (b) how many database files have open write transactions, not 
  1650   1650     ** including the temp database. (b) is important because if more than 
  1651   1651     ** one database file has an open write transaction, a master journal
  1652   1652     ** file is required for an atomic commit.
  1653   1653     */ 
  1654         -  for(i=0; i<db->nDb; i++){ 
         1654  +  for(i=0; i<db->nDb; i++){
  1655   1655       Btree *pBt = db->aDb[i].pBt;
  1656   1656       if( sqlite3BtreeIsInTrans(pBt) ){
  1657   1657         needXcommit = 1;
  1658   1658         if( i!=1 ) nTrans++;
  1659   1659       }
  1660   1660     }
  1661   1661   

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, profile, progress, rekey, restore, rollback_hook, status, timeout, total_changes, trace, transaction, unlock_notify, update_hook, or version}}
           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, log_hook, nullvalue, onecolumn, profile, progress, rekey, restore, rollback_hook, status, timeout, total_changes, trace, transaction, unlock_notify, update_hook, or version}}
    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

Added test/walhook.test.

            1  +# 2010 April 19
            2  +#
            3  +# The author disclaims copyright to this source code.  In place of
            4  +# a legal notice, here is a blessing:
            5  +#
            6  +#    May you do good and not evil.
            7  +#    May you find forgiveness for yourself and forgive others.
            8  +#    May you share freely, never taking more than you give.
            9  +#
           10  +#***********************************************************************
           11  +# This file implements regression tests for SQLite library.  The
           12  +# focus of this file is testing the operation of the library in
           13  +# "PRAGMA journal_mode=WAL" mode.
           14  +#
           15  +
           16  +set testdir [file dirname $argv0]
           17  +source $testdir/tester.tcl
           18  +
           19  +proc sqlite3_wal {args} {
           20  +  eval sqlite3 $args
           21  +  [lindex $args 0] eval { 
           22  +    PRAGMA journal_mode = wal;
           23  +    PRAGMA synchronous = normal;
           24  +    PRAGMA page_size = 1024;
           25  +  }
           26  +}
           27  +sqlite3_wal db test.db
           28  +db log_hook log_hook
           29  +
           30  +set ::log_hook [list]
           31  +proc log_hook {zDb nEntry} {
           32  +  lappend ::log_hook $zDb $nEntry
           33  +  return 0
           34  +}
           35  +
           36  +do_test walhook-1.1 {
           37  +  execsql { CREATE TABLE t1(i PRIMARY KEY, j) }
           38  +  set ::log_hook
           39  +} {main 3}
           40  +do_test walhook-1.2 {
           41  +  set ::log_hook [list]
           42  +  execsql { INSERT INTO t1 VALUES(1, 'one') }
           43  +  set ::log_hook
           44  +} {main 5}
           45  +do_test walhook-1.3 {
           46  +  proc log_hook {args} { return 1 }
           47  +  execsql { INSERT INTO t1 VALUES(2, 'two') }
           48  +  file size test.db
           49  +} [expr 3*1024]
           50  +
           51  +do_test walhook-1.4 {
           52  +  proc log_hook {zDb nEntry} { 
           53  +    execsql { PRAGMA checkpoint }
           54  +    return 0
           55  +  }
           56  +  execsql { CREATE TABLE t2(a, b) }
           57  +  file size test.db
           58  +} [expr 4*1024]
           59  +
           60  +do_test walhook-1.5 {
           61  +  sqlite3_wal db2 test.db
           62  +  proc log_hook {zDb nEntry} { 
           63  +    execsql { PRAGMA checkpoint } db2
           64  +    return 0
           65  +  }
           66  +  execsql { CREATE TABLE t3(a PRIMARY KEY, b) }
           67  +  file size test.db
           68  +} [expr 6*1024]
           69  +
           70  +catch { db2 close }
           71  +catch { db close }
           72  +finish_test
           73  +