SQLite

Check-in [1b14195e05]
Login

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

Overview
Comment:Change the behavior of the sqlite3_wal_hook() callback. It should now return SQLITE_OK or an error code and the error code is propagated back up the stack. If a checkpoint is desired, the callback should invoke sqlite3_wal_callback() itself.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 1b14195e05fe5551992a39246ec3bcf6a33bbfac
User & Date: drh 2010-05-05 20:00:26.000
Context
2010-05-06
07:43
Add tests for handling errors returned by xShm VFS methods. (check-in: fbbcacb137 user: dan tags: trunk)
2010-05-05
20:00
Change the behavior of the sqlite3_wal_hook() callback. It should now return SQLITE_OK or an error code and the error code is propagated back up the stack. If a checkpoint is desired, the callback should invoke sqlite3_wal_callback() itself. (check-in: 1b14195e05 user: drh tags: trunk)
19:09
Further simplifications to the SHM locking logic. Remove the SQLITE_SHM_QUERY option. Unify the WRITE to READ and RECOVER to READ transitions. (check-in: e9bfa6e4ce user: drh tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/main.c.
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204



1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
  sqlite3_mutex_leave(db->mutex);
  return pRet;
}

#ifndef SQLITE_OMIT_WAL
/*
** The sqlite3_wal_hook() callback registered by sqlite3_wal_autocheckpoint().
** Return non-zero, indicating to the caller that a checkpoint should be run,
** if the number of frames in the log file is greater than 
** sqlite3.pWalArg cast to an integer (the value configured by
** wal_autocheckpoint()).
*/ 
int sqlite3WalDefaultHook(
  void *p,               /* Argument */
  sqlite3 *db,           /* Connection */
  const char *zNotUsed,  /* Database */
  int nFrame             /* Size of WAL */
){
  UNUSED_PARAMETER(zNotUsed);
  return ( nFrame>=SQLITE_PTR_TO_INT(p));



}
#endif /* SQLITE_OMIT_WAL */

/*
** Configure an sqlite3_wal_hook() callback to automatically checkpoint
** a database after committing a transaction if there are nFrame or
** more frames in the log file. Passing zero or a negative value as the
** nFrame parameter disables automatic checkpoints entirely.
**
** The callback registered by this function replaces any existing callback
** registered using sqlite3_wal_hook(). Likewise, registering a callback
** using sqlite3_wal_hook() disables the automatic checkpoint mechanism
** configured by this function.
*/
int sqlite3_wal_autocheckpoint(sqlite3 *db, int nFrame){
#ifndef SQLITE_OMIT_WAL
  sqlite3_mutex_enter(db->mutex);
  if( nFrame>0 ){
    sqlite3_wal_hook(db, sqlite3WalDefaultHook, SQLITE_INT_TO_PTR(nFrame));
  }else{
    sqlite3_wal_hook(db, 0, 0);
  }
  sqlite3_mutex_leave(db->mutex);
#endif
  return SQLITE_OK;
}

/*
** Register a callback to be invoked each time a transaction is written
** into the write-ahead-log by this database connection.







<
|
|



|

|


<
|
>
>
>
















<





<







1185
1186
1187
1188
1189
1190
1191

1192
1193
1194
1195
1196
1197
1198
1199
1200
1201

1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221

1222
1223
1224
1225
1226

1227
1228
1229
1230
1231
1232
1233
  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()).
*/ 
int sqlite3WalDefaultHook(
  void *pClientData,     /* Argument */
  sqlite3 *db,           /* Connection */
  const char *zDb,       /* Database */
  int nFrame             /* Size of WAL */
){

  if( nFrame>=SQLITE_PTR_TO_INT(pClientData) ){
    sqlite3_wal_checkpoint(db, zDb);
  }
  return SQLITE_OK;
}
#endif /* SQLITE_OMIT_WAL */

/*
** Configure an sqlite3_wal_hook() callback to automatically checkpoint
** a database after committing a transaction if there are nFrame or
** more frames in the log file. Passing zero or a negative value as the
** nFrame parameter disables automatic checkpoints entirely.
**
** The callback registered by this function replaces any existing callback
** registered using sqlite3_wal_hook(). Likewise, registering a callback
** using sqlite3_wal_hook() disables the automatic checkpoint mechanism
** configured by this function.
*/
int sqlite3_wal_autocheckpoint(sqlite3 *db, int nFrame){
#ifndef SQLITE_OMIT_WAL

  if( nFrame>0 ){
    sqlite3_wal_hook(db, sqlite3WalDefaultHook, SQLITE_INT_TO_PTR(nFrame));
  }else{
    sqlite3_wal_hook(db, 0, 0);
  }

#endif
  return SQLITE_OK;
}

/*
** Register a callback to be invoked each time a transaction is written
** into the write-ahead-log by this database connection.
Changes to src/sqlite.h.in.
5776
5777
5778
5779
5780
5781
5782
5783
5784

5785
5786
5787
5788
5789
5790
5791
5792
** is a copy of the third parameter passed to sqlite3_wal_hook() when
** registering the callback. The second is a copy of the database handle.
** The third parameter is the name of the database that was written to -
** either "main" or the name of an ATTACHed database. The fourth parameter
** is the number of pages currently in the log file, including those that
** were just committed.
**
** If an invocation of the callback function returns non-zero, then a
** checkpoint is automatically run on the database. If zero is returned,

** no special action is taken.
**
** A single database handle may have at most a single log callback 
** registered at one time. Calling [sqlite3_wal_hook()] replaces any
** previously registered log callback. 
*/
void *sqlite3_wal_hook(
  sqlite3*, 







|
|
>
|







5776
5777
5778
5779
5780
5781
5782
5783
5784
5785
5786
5787
5788
5789
5790
5791
5792
5793
** is a copy of the third parameter passed to sqlite3_wal_hook() when
** registering the callback. The second is a copy of the database handle.
** The third parameter is the name of the database that was written to -
** either "main" or the name of an ATTACHed database. The fourth parameter
** is the number of pages currently in the log file, including those that
** were just committed.
**
** The callback function should normally return SQLITE_OK.  If an error
** code is returned, that error will propagate back up through the
** SQLite code base to cause the statement that provoked the callback
** to fail.
**
** A single database handle may have at most a single log callback 
** registered at one time. Calling [sqlite3_wal_hook()] replaces any
** previously registered log callback. 
*/
void *sqlite3_wal_hook(
  sqlite3*, 
Changes to src/tclsqlite.c.
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
  char *zProgress;           /* The progress callback routine */
  char *zAuth;               /* The authorization callback routine */
  int disableAuth;           /* Disable the authorizer if it exists */
  char *zNull;               /* Text to substitute for an SQL NULL value */
  SqlFunc *pFunc;            /* List of SQL functions */
  Tcl_Obj *pUpdateHook;      /* Update hook script (if any) */
  Tcl_Obj *pRollbackHook;    /* Rollback hook script (if any) */
  Tcl_Obj *pLogHook;         /* WAL hook script (if any) */
  Tcl_Obj *pUnlockNotify;    /* Unlock notify script (if any) */
  SqlCollate *pCollate;      /* List of SQL collation functions */
  int rc;                    /* Return code of most recent sqlite3_exec() */
  Tcl_Obj *pCollateNeeded;   /* Collation needed script */
  SqlPreparedStmt *stmtList; /* List of prepared statements*/
  SqlPreparedStmt *stmtLast; /* Last statement in the list */
  int maxStmt;               /* The next maximum number of stmtList */







|







119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
  char *zProgress;           /* The progress callback routine */
  char *zAuth;               /* The authorization callback routine */
  int disableAuth;           /* Disable the authorizer if it exists */
  char *zNull;               /* Text to substitute for an SQL NULL value */
  SqlFunc *pFunc;            /* List of SQL functions */
  Tcl_Obj *pUpdateHook;      /* Update hook script (if any) */
  Tcl_Obj *pRollbackHook;    /* Rollback hook script (if any) */
  Tcl_Obj *pWalHook;         /* WAL hook script (if any) */
  Tcl_Obj *pUnlockNotify;    /* Unlock notify script (if any) */
  SqlCollate *pCollate;      /* List of SQL collation functions */
  int rc;                    /* Return code of most recent sqlite3_exec() */
  Tcl_Obj *pCollateNeeded;   /* Collation needed script */
  SqlPreparedStmt *stmtList; /* List of prepared statements*/
  SqlPreparedStmt *stmtLast; /* Last statement in the list */
  int maxStmt;               /* The next maximum number of stmtList */
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
  }
  if( pDb->pUpdateHook ){
    Tcl_DecrRefCount(pDb->pUpdateHook);
  }
  if( pDb->pRollbackHook ){
    Tcl_DecrRefCount(pDb->pRollbackHook);
  }
  if( pDb->pLogHook ){
    Tcl_DecrRefCount(pDb->pLogHook);
  }
  if( pDb->pCollateNeeded ){
    Tcl_DecrRefCount(pDb->pCollateNeeded);
  }
  Tcl_Free((char*)pDb);
}








|
|







482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
  }
  if( pDb->pUpdateHook ){
    Tcl_DecrRefCount(pDb->pUpdateHook);
  }
  if( pDb->pRollbackHook ){
    Tcl_DecrRefCount(pDb->pRollbackHook);
  }
  if( pDb->pWalHook ){
    Tcl_DecrRefCount(pDb->pWalHook);
  }
  if( pDb->pCollateNeeded ){
    Tcl_DecrRefCount(pDb->pCollateNeeded);
  }
  Tcl_Free((char*)pDb);
}

589
590
591
592
593
594
595



596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
  SqliteDb *pDb = (SqliteDb*)clientData;
  assert(pDb->pRollbackHook);
  if( TCL_OK!=Tcl_EvalObjEx(pDb->interp, pDb->pRollbackHook, 0) ){
    Tcl_BackgroundError(pDb->interp);
  }
}




static int DbLogHandler(
  void *clientData, 
  sqlite3 *db, 
  const char *zDb, 
  int nEntry
){
  int ret = 0;
  Tcl_Obj *p;
  SqliteDb *pDb = (SqliteDb*)clientData;
  Tcl_Interp *interp = pDb->interp;
  assert(pDb->pLogHook);

  p = Tcl_DuplicateObj(pDb->pLogHook);
  Tcl_IncrRefCount(p);
  Tcl_ListObjAppendElement(interp, p, Tcl_NewStringObj(zDb, -1));
  Tcl_ListObjAppendElement(interp, p, Tcl_NewIntObj(nEntry));
  if( TCL_OK!=Tcl_EvalObjEx(interp, p, 0) 
   || TCL_OK!=Tcl_GetIntFromObj(interp, Tcl_GetObjResult(interp), &ret)
  ){
    Tcl_BackgroundError(interp);







>
>
>
|





|



|

|







589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
  SqliteDb *pDb = (SqliteDb*)clientData;
  assert(pDb->pRollbackHook);
  if( TCL_OK!=Tcl_EvalObjEx(pDb->interp, pDb->pRollbackHook, 0) ){
    Tcl_BackgroundError(pDb->interp);
  }
}

/*
** This procedure handles wal_hook callbacks.
*/
static int DbWalHandler(
  void *clientData, 
  sqlite3 *db, 
  const char *zDb, 
  int nEntry
){
  int ret = SQLITE_OK;
  Tcl_Obj *p;
  SqliteDb *pDb = (SqliteDb*)clientData;
  Tcl_Interp *interp = pDb->interp;
  assert(pDb->pWalHook);

  p = Tcl_DuplicateObj(pDb->pWalHook);
  Tcl_IncrRefCount(p);
  Tcl_ListObjAppendElement(interp, p, Tcl_NewStringObj(zDb, -1));
  Tcl_ListObjAppendElement(interp, p, Tcl_NewIntObj(nEntry));
  if( TCL_OK!=Tcl_EvalObjEx(interp, p, 0) 
   || TCL_OK!=Tcl_GetIntFromObj(interp, Tcl_GetObjResult(interp), &ret)
  ){
    Tcl_BackgroundError(interp);
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
    /* set ppHook to point at pUpdateHook or pRollbackHook, depending on 
    ** whether [$db update_hook] or [$db rollback_hook] was invoked.
    */
    Tcl_Obj **ppHook; 
    if( choice==DB_UPDATE_HOOK ){
      ppHook = &pDb->pUpdateHook;
    }else if( choice==DB_WAL_HOOK ){
      ppHook = &pDb->pLogHook;
    }else{
      ppHook = &pDb->pRollbackHook;
    }

    if( objc!=2 && objc!=3 ){
       Tcl_WrongNumArgs(interp, 2, objv, "?SCRIPT?");
       return TCL_ERROR;







|







2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
    /* set ppHook to point at pUpdateHook or pRollbackHook, depending on 
    ** whether [$db update_hook] or [$db rollback_hook] was invoked.
    */
    Tcl_Obj **ppHook; 
    if( choice==DB_UPDATE_HOOK ){
      ppHook = &pDb->pUpdateHook;
    }else if( choice==DB_WAL_HOOK ){
      ppHook = &pDb->pWalHook;
    }else{
      ppHook = &pDb->pRollbackHook;
    }

    if( objc!=2 && objc!=3 ){
       Tcl_WrongNumArgs(interp, 2, objv, "?SCRIPT?");
       return TCL_ERROR;
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
        *ppHook = objv[2];
        Tcl_IncrRefCount(*ppHook);
      }
    }

    sqlite3_update_hook(pDb->db, (pDb->pUpdateHook?DbUpdateHandler:0), pDb);
    sqlite3_rollback_hook(pDb->db,(pDb->pRollbackHook?DbRollbackHandler:0),pDb);
    sqlite3_wal_hook(pDb->db,(pDb->pLogHook?DbLogHandler:0),pDb);

    break;
  }

  /*    $db version
  **
  ** Return the version string for this database.







|







2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
        *ppHook = objv[2];
        Tcl_IncrRefCount(*ppHook);
      }
    }

    sqlite3_update_hook(pDb->db, (pDb->pUpdateHook?DbUpdateHandler:0), pDb);
    sqlite3_rollback_hook(pDb->db,(pDb->pRollbackHook?DbRollbackHandler:0),pDb);
    sqlite3_wal_hook(pDb->db,(pDb->pWalHook?DbWalHandler:0),pDb);

    break;
  }

  /*    $db version
  **
  ** Return the version string for this database.
Changes to src/vdbeapi.c.
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
  int rc = SQLITE_OK;
#ifndef SQLITE_OMIT_WAL
  int i;
  for(i=0; i<db->nDb; i++){
    Btree *pBt = db->aDb[i].pBt;
    if( pBt ){
      int nEntry = sqlite3PagerWalCallback(sqlite3BtreePager(pBt));
      if( db->xWalCallback && nEntry>0 && rc==SQLITE_OK
       && db->xWalCallback(db->pWalArg, db, db->aDb[i].zName, nEntry)
      ){
        rc = sqlite3Checkpoint(db, i);
      }
    }
  }
#endif
  return rc;
}








|
|
<
<







314
315
316
317
318
319
320
321
322


323
324
325
326
327
328
329
  int rc = SQLITE_OK;
#ifndef SQLITE_OMIT_WAL
  int i;
  for(i=0; i<db->nDb; i++){
    Btree *pBt = db->aDb[i].pBt;
    if( pBt ){
      int nEntry = sqlite3PagerWalCallback(sqlite3BtreePager(pBt));
      if( db->xWalCallback && nEntry>0 && rc==SQLITE_OK ){
        rc = db->xWalCallback(db->pWalArg, db, db->aDb[i].zName, nEntry);


      }
    }
  }
#endif
  return rc;
}

Changes to test/walhook.test.
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

do_test walhook-1.2 {
  set ::wal_hook [list]
  execsql { INSERT INTO t1 VALUES(1, 'one') }
  set ::wal_hook
} {main 5}
do_test walhook-1.3 {
  proc wal_hook {args} { return 1 }
  execsql { INSERT INTO t1 VALUES(2, 'two') }
  file size test.db
} [expr 3*1024]
do_test walhook-1.4 {
  proc wal_hook {zDb nEntry} { 
    execsql { PRAGMA wal_checkpoint }
    return 0







|







45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

do_test walhook-1.2 {
  set ::wal_hook [list]
  execsql { INSERT INTO t1 VALUES(1, 'one') }
  set ::wal_hook
} {main 5}
do_test walhook-1.3 {
  proc wal_hook {args} { db eval {PRAGMA wal_checkpoint}; return 0 }
  execsql { INSERT INTO t1 VALUES(2, 'two') }
  file size test.db
} [expr 3*1024]
do_test walhook-1.4 {
  proc wal_hook {zDb nEntry} { 
    execsql { PRAGMA wal_checkpoint }
    return 0
Changes to test/walthread.test.
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
    INSERT INTO t1 VALUES(0, 0, 0);
  }
} -thread t 10 {

  set nextwrite $E(pid)

  proc wal_hook {zDb nEntry} {
    if {$nEntry>10} { return 1 }
    return 0
  }
  db wal_hook wal_hook

  while {[tt_continue]} {
    set max 0
    while { $max != ($nextwrite-1) && [tt_continue] } {







|







386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
    INSERT INTO t1 VALUES(0, 0, 0);
  }
} -thread t 10 {

  set nextwrite $E(pid)

  proc wal_hook {zDb nEntry} {
    if {$nEntry>10} {db eval {PRAGMA wal_checkpoint}}
    return 0
  }
  db wal_hook wal_hook

  while {[tt_continue]} {
    set max 0
    while { $max != ($nextwrite-1) && [tt_continue] } {
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
  # busy-handler is not required. Disable it to check that this is true.
  db busy {}
  while {[tt_continue]} integrity_check
  set {} ok
} -thread w 1 {

  proc wal_hook {zDb nEntry} {
    if {$nEntry>15} { return 1 }
    return 0
  }
  db wal_hook wal_hook
  set row 1
  while {[tt_continue]} {
    db eval { REPLACE INTO t1 VALUES($row, randomblob(300)) }
    incr row







|







439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
  # busy-handler is not required. Disable it to check that this is true.
  db busy {}
  while {[tt_continue]} integrity_check
  set {} ok
} -thread w 1 {

  proc wal_hook {zDb nEntry} {
    if {$nEntry>15} {db eval {PRAGMA wal_checkpoint}}
    return 0
  }
  db wal_hook wal_hook
  set row 1
  while {[tt_continue]} {
    db eval { REPLACE INTO t1 VALUES($row, randomblob(300)) }
    incr row