/ Check-in [9803196d]
Login

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

Overview
Comment:Add the sqlite3_wal_checkpoint() and sqlite3_wal_autocheckpoint() APIs.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | wal
Files: files | file ages | folders
SHA1: 9803196dec85e3aa4105cc477e9cfe98d370e486
User & Date: dan 2010-05-03 08:04:49
Context
2010-05-03
08:19
Merge two wal leaves. check-in: 23c0e6c3 user: dan tags: wal
08:04
Add the sqlite3_wal_checkpoint() and sqlite3_wal_autocheckpoint() APIs. check-in: 9803196d user: dan tags: wal
2010-05-01
17:57
Define an invariant to guarantee deadlock-free operation of SHM in os_unix.c and check that invariant with assert() statements. check-in: 6af2dca7 user: drh tags: wal
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/main.c.

1181
1182
1183
1184
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
....
1763
1764
1765
1766
1767
1768
1769


1770
1771
1772
1773
1774
1775
1776
  sqlite3_mutex_enter(db->mutex);
  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 transaction is written
** into the write-ahead-log by this database connection.
*/
void *sqlite3_wal_hook(
  sqlite3 *db,                    /* Attach the hook to this db handle */
  int(*xCallback)(void *, sqlite3*, const char*, int),
  void *pArg                      /* First argument passed to xCallback() */
){
#ifndef SQLITE_OMIT_WAL
  void *pRet;
  sqlite3_mutex_enter(db->mutex);
  pRet = db->pWalArg;
  db->xWalCallback = xCallback;
  db->pWalArg = pArg;
  sqlite3_mutex_leave(db->mutex);
  return pRet;















#else


















































  return 0;
#endif
}




/*
** This function returns true if main-memory should be used instead of
** a temporary file for transient pager files and statement journals.
** The value returned depends on the value of db->temp_store (runtime
** parameter) and the compile time value of SQLITE_TEMP_STORE. The
** following table describes the relationship between these two values
................................................................................
  sqlite3PagerLockingMode(sqlite3BtreePager(db->aDb[0].pBt),
                          SQLITE_DEFAULT_LOCKING_MODE);
#endif

  /* Enable the lookaside-malloc subsystem */
  setupLookaside(db, 0, sqlite3GlobalConfig.szLookaside,
                        sqlite3GlobalConfig.nLookaside);



opendb_out:
  if( db ){
    assert( db->mutex!=0 || isThreadsafe==0 || sqlite3GlobalConfig.bFullMutex==0 );
    sqlite3_mutex_leave(db->mutex);
  }
  rc = sqlite3_errcode(db);







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>










<







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

<

>
>
>







 







>
>







1181
1182
1183
1184
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
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307

1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
....
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
  sqlite3_mutex_enter(db->mutex);
  pRet = db->pRollbackArg;
  db->xRollbackCallback = xCallback;
  db->pRollbackArg = pArg;
  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.nDefaultCheckpoint (the value configured by wal_autocheckpoint()).
*/ 
static int defaultWalHook(void *p, sqlite3 *db, const char *z, int nFrame){
  UNUSED_PARAMETER(p);
  UNUSED_PARAMETER(z);
  return ( nFrame>=db->nDefaultCheckpoint );
}

/*
** 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){
  sqlite3_mutex_enter(db->mutex);
  if( nFrame>0 ){
    db->nDefaultCheckpoint = nFrame;
    sqlite3_wal_hook(db, defaultWalHook, 0);
  }else{
    sqlite3_wal_hook(db, 0, 0);
  }
  sqlite3_mutex_leave(db->mutex);
  return SQLITE_OK;
}

/*
** Register a callback to be invoked each time a transaction is written
** into the write-ahead-log by this database connection.
*/
void *sqlite3_wal_hook(
  sqlite3 *db,                    /* Attach the hook to this db handle */
  int(*xCallback)(void *, sqlite3*, const char*, int),
  void *pArg                      /* First argument passed to xCallback() */
){

  void *pRet;
  sqlite3_mutex_enter(db->mutex);
  pRet = db->pWalArg;
  db->xWalCallback = xCallback;
  db->pWalArg = pArg;
  sqlite3_mutex_leave(db->mutex);
  return pRet;
}

/*
** Checkpoint database zDb. If zDb is NULL, the main database is checkpointed.
*/
int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb){
  int rc;                         /* Return code */
  int iDb = 0;                    /* sqlite3.aDb[] index of db to checkpoint */

  sqlite3_mutex_enter(db->mutex);
  if( zDb ){
    iDb = sqlite3FindDbName(db, zDb);
  }
  if( iDb<0 ){
    rc = SQLITE_ERROR;
  }else{
    rc = sqlite3Checkpoint(db, iDb);
  }
  sqlite3_mutex_leave(db->mutex);

  return rc;
}

/*
** Run a checkpoint on database iDb. This is a no-op if database iDb is
** not currently open in WAL mode.
**
** If a transaction is open at either the database handle (db) or b-tree
** level, this function returns SQLITE_LOCKED and a checkpoint is not
** attempted. If an error occurs while running the checkpoint, an SQLite
** error code is returned (i.e. SQLITE_IOERR). Otherwise, SQLITE_OK.
**
** The mutex on database handle db should be held by the caller. The mutex
** associated with the specific b-tree being checkpointed is taken by
** this function while the checkpoint is running.
*/
int sqlite3Checkpoint(sqlite3 *db, int iDb){
  Btree *pBt;                     /* Btree handle to checkpoint */
  int rc;                         /* Return code */

  assert( sqlite3_mutex_held(db->mutex) );

  pBt = db->aDb[iDb].pBt;
  if( sqlite3BtreeIsInReadTrans(pBt) ){
    rc = SQLITE_LOCKED;
  }else{
    sqlite3BtreeEnter(pBt);
    rc = sqlite3PagerCheckpoint(sqlite3BtreePager(pBt));
    sqlite3BtreeLeave(pBt);
  }

  return rc;
}
#else /* ifndef SQLITE_OMIT_WAL */
/*
** If SQLITE_OMIT_WAL is defined, the following API functions are no-ops:
**
**   sqlite3_wal_hook()
**   sqlite3_wal_checkpoint()
**   sqlite3_wal_autocheckpoint()
*/
void *sqlite3_wal_hook(
  sqlite3 *x,
  int(*xCallback)(void *, sqlite3*, const char*, int),
  void *pArg
){
  return 0;

}
int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb){ return SQLITE_OK; }
int sqlite3_wal_autocheckpoint(sqlite3 *db, int nFrame){ return SQLITE_OK; }
#endif

/*
** This function returns true if main-memory should be used instead of
** a temporary file for transient pager files and statement journals.
** The value returned depends on the value of db->temp_store (runtime
** parameter) and the compile time value of SQLITE_TEMP_STORE. The
** following table describes the relationship between these two values
................................................................................
  sqlite3PagerLockingMode(sqlite3BtreePager(db->aDb[0].pBt),
                          SQLITE_DEFAULT_LOCKING_MODE);
#endif

  /* Enable the lookaside-malloc subsystem */
  setupLookaside(db, 0, sqlite3GlobalConfig.szLookaside,
                        sqlite3GlobalConfig.nLookaside);

  sqlite3_wal_autocheckpoint(db, SQLITE_DEFAULT_CACHE_SIZE);

opendb_out:
  if( db ){
    assert( db->mutex!=0 || isThreadsafe==0 || sqlite3GlobalConfig.bFullMutex==0 );
    sqlite3_mutex_leave(db->mutex);
  }
  rc = sqlite3_errcode(db);

Changes to src/pragma.c.

1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
#ifndef SQLITE_OMIT_WAL
  /*
  **   PRAGMA [database.]checkpoint
  **
  ** Checkpoint the database.
  */
  if( sqlite3StrICmp(zLeft, "checkpoint")==0 ){
    sqlite3VdbeUsesBtree(v, iDb);
    sqlite3VdbeAddOp3(v, OP_Checkpoint, iDb, 0, 0);
  }else
#endif

#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
  /*
  ** Report the current state of file logs for all databases







<







1403
1404
1405
1406
1407
1408
1409

1410
1411
1412
1413
1414
1415
1416
#ifndef SQLITE_OMIT_WAL
  /*
  **   PRAGMA [database.]checkpoint
  **
  ** Checkpoint the database.
  */
  if( sqlite3StrICmp(zLeft, "checkpoint")==0 ){

    sqlite3VdbeAddOp3(v, OP_Checkpoint, iDb, 0, 0);
  }else
#endif

#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
  /*
  ** Report the current state of file logs for all databases

Changes to src/sqlite.h.in.

5793
5794
5795
5796
5797
5798
5799










5800
5801
5802
5803
5804
5805
5806
5807
5808
5809
5810
5811
*/
void *sqlite3_wal_hook(
  sqlite3*, 
  int(*)(void *,sqlite3*,const char*,int),
  void*
);











/*
** Undo the hack that converts floating point types to integer for
** builds on processors without floating point support.
*/
#ifdef SQLITE_OMIT_FLOATING_POINT
# undef double
#endif

#ifdef __cplusplus
}  /* End of the 'extern "C"' block */
#endif
#endif







>
>
>
>
>
>
>
>
>
>












5793
5794
5795
5796
5797
5798
5799
5800
5801
5802
5803
5804
5805
5806
5807
5808
5809
5810
5811
5812
5813
5814
5815
5816
5817
5818
5819
5820
5821
*/
void *sqlite3_wal_hook(
  sqlite3*, 
  int(*)(void *,sqlite3*,const char*,int),
  void*
);

/*
** CAPI3REF: Configure an auto-checkpoint
*/
int sqlite3_wal_autocheckpoint(sqlite3 *db, int N);

/*
** CAPI3REF: Checkpoint a database
*/
int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb);

/*
** Undo the hack that converts floating point types to integer for
** builds on processors without floating point support.
*/
#ifdef SQLITE_OMIT_FLOATING_POINT
# undef double
#endif

#ifdef __cplusplus
}  /* End of the 'extern "C"' block */
#endif
#endif

Changes to src/sqliteInt.h.

820
821
822
823
824
825
826

827
828
829
830
831
832
833
....
2994
2995
2996
2997
2998
2999
3000

3001
3002
3003
3004
3005
3006
3007
  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);
#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;
  sqlite3_value *pErr;          /* Most recent error message */
................................................................................
int sqlite3TransferBindings(sqlite3_stmt *, sqlite3_stmt *);
int sqlite3Reprepare(Vdbe*);
void sqlite3ExprListCheckLength(Parse*, ExprList*, const char*);
CollSeq *sqlite3BinaryCompareCollSeq(Parse *, Expr *, Expr *);
int sqlite3TempInMemory(const sqlite3*);
VTable *sqlite3GetVTable(sqlite3*, Table*);
const char *sqlite3JournalModename(int);


/* Declarations for functions in fkey.c. All of these are replaced by
** no-op macros if OMIT_FOREIGN_KEY is defined. In this case no foreign
** key functionality is available. If OMIT_TRIGGER is defined but
** OMIT_FOREIGN_KEY is not, only some of the functions are no-oped. In
** this case foreign keys are parsed, but no other functionality is 
** provided (enforcement of FK constraints requires the triggers sub-system).







>







 







>







820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
....
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
  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);
#ifndef SQLITE_OMIT_WAL
  int nDefaultCheckpoint;       /* Value configured by wal_autocheckpoint() */
  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;
  sqlite3_value *pErr;          /* Most recent error message */
................................................................................
int sqlite3TransferBindings(sqlite3_stmt *, sqlite3_stmt *);
int sqlite3Reprepare(Vdbe*);
void sqlite3ExprListCheckLength(Parse*, ExprList*, const char*);
CollSeq *sqlite3BinaryCompareCollSeq(Parse *, Expr *, Expr *);
int sqlite3TempInMemory(const sqlite3*);
VTable *sqlite3GetVTable(sqlite3*, Table*);
const char *sqlite3JournalModename(int);
int sqlite3Checkpoint(sqlite3*, int);

/* Declarations for functions in fkey.c. All of these are replaced by
** no-op macros if OMIT_FOREIGN_KEY is defined. In this case no foreign
** key functionality is available. If OMIT_TRIGGER is defined but
** OMIT_FOREIGN_KEY is not, only some of the functions are no-oped. In
** this case foreign keys are parsed, but no other functionality is 
** provided (enforcement of FK constraints requires the triggers sub-system).

Changes to src/vdbe.c.

5189
5190
5191
5192
5193
5194
5195
5196
5197
5198
5199
5200
5201
5202
5203
5204
5205
5206
5207
5208
#ifndef SQLITE_OMIT_WAL
/* Opcode: Checkpoint P1 * * * *
**
** Checkpoint database P1. This is a no-op if P1 is not currently in
** WAL mode.
*/
case OP_Checkpoint: {
  Btree *pBt;                     /* Btree to checkpoint */

  assert( pOp->p1>=0 && pOp->p1<db->nDb );
  assert( (p->btreeMask & (1<<pOp->p1))!=0 );
  pBt = db->aDb[pOp->p1].pBt;
  rc = sqlite3PagerCheckpoint(sqlite3BtreePager(pBt));
  break;
};  
#endif

/* Opcode: JournalMode P1 P2 P3 * *
**
** Change the journal mode of database P1 to P3. P3 must be one of the







|
<
<
<
<
<







5189
5190
5191
5192
5193
5194
5195
5196





5197
5198
5199
5200
5201
5202
5203
#ifndef SQLITE_OMIT_WAL
/* Opcode: Checkpoint P1 * * * *
**
** Checkpoint database P1. This is a no-op if P1 is not currently in
** WAL mode.
*/
case OP_Checkpoint: {
  rc = sqlite3Checkpoint(db, pOp->p1);





  break;
};  
#endif

/* Opcode: JournalMode P1 P2 P3 * *
**
** Change the journal mode of database P1 to P3. P3 must be one of the

Changes to test/backup.test.

485
486
487
488
489
490
491

492
493
494
495
496
497
498
#
#   1) Backing up file-to-file. The writer writes via an external pager.
#   2) Backing up file-to-file. The writer writes via the same pager as
#      is used by the backup operation.
#   3) Backing up memory-to-file. 
#
set iTest 0

foreach {writer file} {db test.db db3 test.db db :memory:} {
  incr iTest
  catch { file delete bak.db }
  sqlite3 db2 bak.db
  catch { file delete $file }
  sqlite3 db $file
  sqlite3 db3 $file







>







485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
#
#   1) Backing up file-to-file. The writer writes via an external pager.
#   2) Backing up file-to-file. The writer writes via the same pager as
#      is used by the backup operation.
#   3) Backing up memory-to-file. 
#
set iTest 0
file delete -force bak.db-wal
foreach {writer file} {db test.db db3 test.db db :memory:} {
  incr iTest
  catch { file delete bak.db }
  sqlite3 db2 bak.db
  catch { file delete $file }
  sqlite3 db $file
  sqlite3 db3 $file

Changes to test/walthread.test.

458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495


# This test case attempts to provoke a deadlock condition that existed in
# the unix VFS at one point. The problem occurred only while recovering a 
# very large wal file (one that requires a wal-index larger than the 
# initial default allocation of 64KB).
#
# When recovering a log file, mutexes are usually obtained and released
# as follows:
#
#   xShmGet                         ENTER mutexBuf
#   xShmLock(RECOVER)               ENTER mutexRecov
#     <do recovery work>
#   xShmLock(READ)                  LEAVE mutexRecov
#   xShmRelease                     LEAVE mutexBuf         
#
# However, if the initial wal-index allocation needs to be enlarged during
# the recovery process, the mutex operations become:
#
#   xShmGet                         ENTER mutexBuf
#   xShmLock(RECOVER)               ENTER mutexRecov
#     <do first part of recovery work>
#     xShmRelease                   LEAVE mutexBuf
#     xShmSize
#     xShmGet                       ENTER mutexBuf
#     <do second part of recovery work>
#   xShmLock(READ)                  LEAVE mutexRecov
#   xShmRelease                     LEAVE mutexBuf         
#
# If multiple threads attempt the above simultaneously, deadlock can occur.
#
do_thread_test walthread-5 -seconds $seconds(walthread-5) -init {

  proc log_file_size {nFrame pgsz} {
    expr {12 + ($pgsz+16)*$nFrame}
  }

  execsql {







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







458
459
460
461
462
463
464
























465
466
467
468
469
470
471


# This test case attempts to provoke a deadlock condition that existed in
# the unix VFS at one point. The problem occurred only while recovering a 
# very large wal file (one that requires a wal-index larger than the 
# initial default allocation of 64KB).
#
























do_thread_test walthread-5 -seconds $seconds(walthread-5) -init {

  proc log_file_size {nFrame pgsz} {
    expr {12 + ($pgsz+16)*$nFrame}
  }

  execsql {