From: dan Date: Tue, 16 Nov 2010 18:56:51 +0000 (+0000) Subject: Add experimental command "PRAGMA wal_blocking_checkpoint", which uses the busy-handle... X-Git-Tag: version-3.7.6~174^2~8 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=a58f26f93f77ae83021d52ff76738f27a59596b9;p=thirdparty%2Fsqlite.git Add experimental command "PRAGMA wal_blocking_checkpoint", which uses the busy-handler to block until all readers have finished in order to ensure the next writer will be able to wrap around to the start of the log file. FossilOrigin-Name: 7e3fc2c833a5baa08820c499867b6902bdc2ed5a --- diff --git a/manifest b/manifest index d06d475d13..a133dac267 100644 --- a/manifest +++ b/manifest @@ -1,8 +1,5 @@ ------BEGIN PGP SIGNED MESSAGE----- -Hash: SHA1 - -C Use\sthe\sestimated\snumber\sof\srows\scomputed\sfor\ssubqueries\sin\sthe\scost\ncomputations\sfor\souter\squeries. -D 2010-11-16T02:49:16 +C Add\sexperimental\scommand\s"PRAGMA\swal_blocking_checkpoint",\swhich\suses\sthe\sbusy-handler\sto\sblock\suntil\sall\sreaders\shave\sfinished\sin\sorder\sto\sensure\sthe\snext\swriter\swill\sbe\sable\sto\swrap\saround\sto\sthe\sstart\sof\sthe\slog\sfile. +D 2010-11-16T18:56:51 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in e7a59672eaeb04408d1fa8501618d7501a3c5e39 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -122,8 +119,8 @@ F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34 F src/backup.c d5b0137bc20327af08c14772227cc35134839c30 F src/bitvec.c af50f1c8c0ff54d6bdb7a80e2fceca5a93670bef F src/btmutex.c 96a12f50f7a17475155971a241d85ec5171573ff -F src/btree.c 3edab36d03d86c200cb9551467410f975d510aa9 -F src/btree.h 2d1a83ad509047e8cc314fda7e054f99ff52414d +F src/btree.c 444aae4fc60cc57d6c97615358e1020f6884cca6 +F src/btree.h d1144d38d790a8b7b2e215043f8d068f4f37de07 F src/btreeInt.h c424f2f131cc61ddf130f9bd736b3df12c8a51f0 F src/build.c 00a327120d81ace6267e714ae8010c997d55de5d F src/callback.c a1d1b1c9c85415dff013af033e2fed9c8382d33b @@ -144,7 +141,7 @@ F src/journal.c 552839e54d1bf76fb8f7abe51868b66acacf6a0e F src/legacy.c a199d7683d60cef73089e892409113e69c23a99f F src/lempar.c 7f026423f4d71d989e719a743f98a1cbd4e6d99e F src/loadext.c 8af9fcc75708d60b88636ccba38b4a7b3c155c3e -F src/main.c 335d2c7ecb093db7b662d9325fe68fb9087a4814 +F src/main.c 89c658ae9a610a61ff856a110bda50606e9227d6 F src/malloc.c 3d7284cd9346ab6e3945535761e68c23c6cf40ef F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 F src/mem1.c 00bd8265c81abb665c48fea1e0c234eb3b922206 @@ -165,13 +162,13 @@ F src/os_common.h a8f95b81eca8a1ab8593d23e94f8a35f35d4078f F src/os_os2.c 72d0b2e562952a2464308c4ce5f7913ac10bef3e F src/os_unix.c de5be4cdbf3d07018059934eaf7e5d8d594a895c F src/os_win.c 2f90f7bdec714fad51cd31b4ecad3cc1b4bb5aad -F src/pager.c 067ae23d7a370eea6bd529848331c63879570adc -F src/pager.h 8167a1e720d0b7a2790079007128e594010220ad +F src/pager.c 7f7587c2f11126d13ee1925ac8960a9e7ab13e8a +F src/pager.h ad7d8db0fbcee7546dbc02ffe0d0d44ea868ef52 F src/parse.y 12b7ebd61ea54f0e1b1083ff69cc2c8ce9353d58 F src/pcache.c 09d38c44ab275db581f7a2f6ff8b9bc7f8c0faaa F src/pcache.h c683390d50f856d4cd8e24342ae62027d1bb6050 F src/pcache1.c e9578a3beac26f229ee558a4e16c863f2498185f -F src/pragma.c 216d12e4546e65ca6cfcd3221e64573889ae8f34 +F src/pragma.c 66a8b53d1e74635011fbb0bb54b7ecc402684bae F src/prepare.c c2b318037d626fed27905c9446730b560637217a F src/printf.c 8ae5082dd38a1b5456030c3755ec3a392cd51506 F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50 @@ -181,7 +178,7 @@ F src/select.c 550d67688f5e8bc8022faf6d014838afba1415af F src/shell.c 8517fc1f9c59ae4007e6cc8b9af91ab231ea2056 F src/sqlite.h.in f47e09412fc9a129f759fa4d96ef21f4b3d529eb F src/sqlite3ext.h c90bd5507099f62043832d73f6425d8d5c5da754 -F src/sqliteInt.h dd28f6138c74cf4833e032a989b6ff7885798cf6 +F src/sqliteInt.h fe1cb073b2707001985f06dee9ee256247e4d0ce F src/sqliteLimit.h a17dcd3fb775d63b64a43a55c54cb282f9726f44 F src/status.c 496913d4e8441195f6f2a75b1c95993a45b9b30b F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e @@ -230,7 +227,7 @@ F src/update.c 227e6cd512108b84f69421fc6c7aa1b83d60d6e0 F src/utf.c 1baeeac91707a4df97ccc6141ec0f808278af685 F src/util.c cd78524566fe45671863eee78685969a4bfd4e4c F src/vacuum.c 924bd1bcee2dfb05376f79845bd3b4cec7b54b2f -F src/vdbe.c e1aa917961e69f71c80f46ce231b496d3c841ae1 +F src/vdbe.c b86b09beb3dcf2e6d5922acee48b8a1c16b68bfd F src/vdbe.h 4de0efb4b0fdaaa900cf419b35c458933ef1c6d2 F src/vdbeInt.h 7f4cf1b2b69bef3a432b1f23dfebef57275436b4 F src/vdbeapi.c 5368714fa750270cf6430160287c21adff44582d @@ -239,8 +236,8 @@ F src/vdbeblob.c e0ce3c54cc0c183af2ec67b63a289acf92251df4 F src/vdbemem.c 23723a12cd3ba7ab3099193094cbb2eb78956aa9 F src/vdbetrace.c 864cef96919323482ebd9986f2132435115e9cc2 F src/vtab.c b297e8fa656ab5e66244ab15680d68db0adbec30 -F src/wal.c f26b8d297bd11cb792e609917f9d4c6718ac8e0e -F src/wal.h c1aac6593a0b02b15dc625987e619edeab39292e +F src/wal.c 400624ce58acce44f0bf0d47ed2f435da290fb04 +F src/wal.h d5bbc11242d7fd14e9dc6a74f68d3ccaf01a9e48 F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f F src/where.c d5cc65f51661a038a2c6a663a945d5cf4c277b81 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 @@ -830,7 +827,8 @@ F test/wal.test 70227190e713b3e7eb2a7d5ec3510b66db01f327 F test/wal2.test c794b8b257af54190bb913678ad3984cbf3311b9 F test/wal3.test 957a5f2a8fe8a6ff01de1a15285ecf2f376fcaf8 F test/wal4.test 3404b048fa5e10605facaf70384e6d2943412e30 -F test/wal_common.tcl 895d76138043b86bdccf36494054bdabcf65837b +F test/wal5.test e0f1abdff4f76d3a8531f5d0f4cb237e5eff891c +F test/wal_common.tcl a98f17fba96206122eff624db0ab13ec377be4fe F test/walbak.test 4df1c7369da0301caeb9a48fa45997fd592380e4 F test/walbig.test e882bc1d014afffbfa2b6ba36e0f07d30a633ad0 F test/walcksum.test a37b36375c595e61bdb7e1ec49b5f0979b6fc7ce @@ -889,14 +887,10 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f -P d52b593978aa1776af7aeb957c4f8df0c5cb7e43 -R d49dbce342d6074d2c08ca15a1061dc2 -U drh -Z 1d0a2a683be8d99ef69f89af3e0fc4c3 ------BEGIN PGP SIGNATURE----- -Version: GnuPG v1.4.6 (GNU/Linux) - -iD8DBQFM4fEvoxKgR168RlERAtLBAJ0duCZxNY8VlhlVLo/NezKrqI5noACdE3GU -LDKZgeEbRVz6H1/mucVVNUQ= -=B2su ------END PGP SIGNATURE----- +P 56bbc539246a6dc9f1ae1edb898db7a4f6f6d322 +R 75a04ae738d792c76538f98d6f9ad655 +T *branch * experimental +T *sym-experimental * +T -sym-trunk * +U dan +Z b1cbb492cf90106bd968e524fdd11e16 diff --git a/manifest.uuid b/manifest.uuid index 5f256ca04e..936ce08f7a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -56bbc539246a6dc9f1ae1edb898db7a4f6f6d322 \ No newline at end of file +7e3fc2c833a5baa08820c499867b6902bdc2ed5a \ No newline at end of file diff --git a/src/btree.c b/src/btree.c index 7e8c39feb6..94100f48fc 100644 --- a/src/btree.c +++ b/src/btree.c @@ -7935,8 +7935,15 @@ int sqlite3BtreeIsInTrans(Btree *p){ ** ** Return SQLITE_LOCKED if this or any other connection has an open ** transaction on the shared-cache the argument Btree is connected to. +** +** If parameter bBlock is true, then the layers below invoke the +** busy-handler callback while waiting for readers to release locks so +** that the entire WAL can be checkpointed. If it is false, then as +** much as possible of the WAL is checkpointed without waiting for readers +** to finish. bBlock is true for "PRAGMA wal_blocking_checkpoint" and false +** for "PRAGMA wal_checkpoint". */ -int sqlite3BtreeCheckpoint(Btree *p){ +int sqlite3BtreeCheckpoint(Btree *p, int bBlock){ int rc = SQLITE_OK; if( p ){ BtShared *pBt = p->pBt; @@ -7944,7 +7951,7 @@ int sqlite3BtreeCheckpoint(Btree *p){ if( pBt->inTransaction!=TRANS_NONE ){ rc = SQLITE_LOCKED; }else{ - rc = sqlite3PagerCheckpoint(pBt->pPager); + rc = sqlite3PagerCheckpoint(pBt->pPager, bBlock); } sqlite3BtreeLeave(p); } diff --git a/src/btree.h b/src/btree.h index 39af03f961..ce86cdabb1 100644 --- a/src/btree.h +++ b/src/btree.h @@ -207,7 +207,7 @@ void sqlite3BtreeCursorList(Btree*); #endif #ifndef SQLITE_OMIT_WAL - int sqlite3BtreeCheckpoint(Btree*); + int sqlite3BtreeCheckpoint(Btree*, int); #endif /* diff --git a/src/main.c b/src/main.c index 25216070d6..7ce43aec5c 100644 --- a/src/main.c +++ b/src/main.c @@ -1361,7 +1361,7 @@ int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb){ rc = SQLITE_ERROR; sqlite3Error(db, SQLITE_ERROR, "unknown database: %s", zDb); }else{ - rc = sqlite3Checkpoint(db, iDb); + rc = sqlite3Checkpoint(db, iDb, 0); sqlite3Error(db, rc, 0); } rc = sqlite3ApiExit(db, rc); @@ -1387,8 +1387,11 @@ int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb){ ** If iDb is passed SQLITE_MAX_ATTACHED, then all attached databases are ** checkpointed. If an error is encountered it is returned immediately - ** no attempt is made to checkpoint any remaining databases. +** +** Parameter bBlock is true for a blocking-checkpoint, false for an +** ordinary, non-blocking, checkpoint. */ -int sqlite3Checkpoint(sqlite3 *db, int iDb){ +int sqlite3Checkpoint(sqlite3 *db, int iDb, int bBlock){ int rc = SQLITE_OK; /* Return code */ int i; /* Used to iterate through attached dbs */ @@ -1396,7 +1399,7 @@ int sqlite3Checkpoint(sqlite3 *db, int iDb){ for(i=0; inDb && rc==SQLITE_OK; i++){ if( i==iDb || iDb==SQLITE_MAX_ATTACHED ){ - rc = sqlite3BtreeCheckpoint(db->aDb[i].pBt); + rc = sqlite3BtreeCheckpoint(db->aDb[i].pBt, bBlock); } } diff --git a/src/pager.c b/src/pager.c index b774af3a5f..29f3bf1eb1 100644 --- a/src/pager.c +++ b/src/pager.c @@ -6516,13 +6516,19 @@ sqlite3_backup **sqlite3PagerBackupPtr(Pager *pPager){ #ifndef SQLITE_OMIT_WAL /* -** This function is called when the user invokes "PRAGMA checkpoint". +** This function is called when the user invokes "PRAGMA wal_checkpoint", +** "PRAGMA wal_blocking_checkpoint" or calls the sqlite3_wal_checkpoint() +** or wal_blocking_checkpoint() API functions. +** +** Parameter bBlock is true for a blocking-checkpoint, false for an +** ordinary, non-blocking, checkpoint. */ -int sqlite3PagerCheckpoint(Pager *pPager){ +int sqlite3PagerCheckpoint(Pager *pPager, int bBlock){ int rc = SQLITE_OK; if( pPager->pWal ){ u8 *zBuf = (u8 *)pPager->pTmpSpace; rc = sqlite3WalCheckpoint(pPager->pWal, + (bBlock ? pPager->xBusyHandler : 0), pPager->pBusyHandlerArg, (pPager->noSync ? 0 : pPager->sync_flags), pPager->pageSize, zBuf ); diff --git a/src/pager.h b/src/pager.h index c12afa7b8b..e0396e99aa 100644 --- a/src/pager.h +++ b/src/pager.h @@ -138,7 +138,7 @@ int sqlite3PagerOpenSavepoint(Pager *pPager, int n); int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint); int sqlite3PagerSharedLock(Pager *pPager); -int sqlite3PagerCheckpoint(Pager *pPager); +int sqlite3PagerCheckpoint(Pager *pPager, int); int sqlite3PagerWalSupported(Pager *pPager); int sqlite3PagerWalCallback(Pager *pPager); int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen); diff --git a/src/pragma.c b/src/pragma.c index 362e77f29e..7324e1bde8 100644 --- a/src/pragma.c +++ b/src/pragma.c @@ -1394,12 +1394,18 @@ void sqlite3Pragma( #ifndef SQLITE_OMIT_WAL /* ** PRAGMA [database.]wal_checkpoint + ** PRAGMA [database.]wal_blocking_checkpoint ** ** Checkpoint the database. */ - if( sqlite3StrICmp(zLeft, "wal_checkpoint")==0 ){ + if( sqlite3StrICmp(zLeft, "wal_checkpoint")==0 + || sqlite3StrICmp(zLeft, "wal_blocking_checkpoint")==0 + ){ + int bBlock = (zLeft[14]!=0); + int iBt = (pId2->z?iDb:SQLITE_MAX_ATTACHED); + assert( bBlock==(sqlite3StrICmp(zLeft, "wal_checkpoint")!=0) ); if( sqlite3ReadSchema(pParse) ) goto pragma_out; - sqlite3VdbeAddOp3(v, OP_Checkpoint, pId2->z?iDb:SQLITE_MAX_ATTACHED, 0, 0); + sqlite3VdbeAddOp2(v, OP_Checkpoint, iBt, bBlock); }else /* diff --git a/src/sqliteInt.h b/src/sqliteInt.h index c02a0e4487..8b4fe599eb 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -3036,7 +3036,7 @@ CollSeq *sqlite3BinaryCompareCollSeq(Parse *, Expr *, Expr *); int sqlite3TempInMemory(const sqlite3*); VTable *sqlite3GetVTable(sqlite3*, Table*); const char *sqlite3JournalModename(int); -int sqlite3Checkpoint(sqlite3*, int); +int sqlite3Checkpoint(sqlite3*, int, int); int sqlite3WalDefaultHook(void*,sqlite3*,const char*,int); /* Declarations for functions in fkey.c. All of these are replaced by diff --git a/src/vdbe.c b/src/vdbe.c index 02d1a406c9..dcff83f17a 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -5216,13 +5216,13 @@ case OP_AggFinal: { } #ifndef SQLITE_OMIT_WAL -/* Opcode: Checkpoint P1 * * * * +/* Opcode: Checkpoint P1 P2 * * * ** ** Checkpoint database P1. This is a no-op if P1 is not currently in -** WAL mode. +** WAL mode. If P2 is non-zero, this is a blocking checkpoint. */ case OP_Checkpoint: { - rc = sqlite3Checkpoint(db, pOp->p1); + rc = sqlite3Checkpoint(db, pOp->p1, pOp->p2); break; }; #endif diff --git a/src/wal.c b/src/wal.c index 3b217908b7..5ff9f227e0 100644 --- a/src/wal.c +++ b/src/wal.c @@ -1523,6 +1523,26 @@ static int walIteratorInit(Wal *pWal, WalIterator **pp){ return rc; } +/* +** Attempt to obtain the exclusive WAL lock defined by parameters lockIdx and +** n. If the attempt fails and parameter xBusy is not NULL, then it is a +** busy-handler function. Invoke it and retry the lock until either the +** lock is successfully obtained or the busy-handler returns 0. +*/ +static int walBusyLock( + Wal *pWal, /* WAL connection */ + int (*xBusy)(void*), /* Function to call when busy */ + void *pBusyArg, /* Context argument for xBusyHandler */ + int lockIdx, /* Offset of first byte to lock */ + int n /* Number of bytes to lock */ +){ + int rc; + do { + rc = walLockExclusive(pWal, lockIdx, n); + }while( xBusy && rc==SQLITE_BUSY && xBusy(pBusyArg) ); + return rc; +} + /* ** Copy as much content as we can from the WAL back into the database file ** in response to an sqlite3_wal_checkpoint() request or the equivalent. @@ -1556,6 +1576,8 @@ static int walIteratorInit(Wal *pWal, WalIterator **pp){ */ static int walCheckpoint( Wal *pWal, /* Wal connection */ + int (*xBusy)(void*), /* Function to call when busy */ + void *pBusyArg, /* Context argument for xBusyHandler */ int sync_flags, /* Flags for OsSync() (or 0) */ int nBuf, /* Size of zBuf in bytes */ u8 *zBuf /* Temporary buffer to use */ @@ -1593,27 +1615,29 @@ static int walCheckpoint( ** overwrite database pages that are in use by active readers and thus ** cannot be backfilled from the WAL. */ - mxSafeFrame = pWal->hdr.mxFrame; - mxPage = pWal->hdr.nPage; - pInfo = walCkptInfo(pWal); - for(i=1; iaReadMark[i]; - if( mxSafeFrame>=y ){ - assert( y<=pWal->hdr.mxFrame ); - rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1); - if( rc==SQLITE_OK ){ - pInfo->aReadMark[i] = READMARK_NOT_USED; - walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1); - }else if( rc==SQLITE_BUSY ){ - mxSafeFrame = y; - }else{ - goto walcheckpoint_out; + do { + mxSafeFrame = pWal->hdr.mxFrame; + mxPage = pWal->hdr.nPage; + pInfo = walCkptInfo(pWal); + for(i=1; iaReadMark[i]; + if( mxSafeFrame>=y ){ + assert( y<=pWal->hdr.mxFrame ); + rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1); + if( rc==SQLITE_OK ){ + pInfo->aReadMark[i] = READMARK_NOT_USED; + walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1); + }else if( rc==SQLITE_BUSY ){ + mxSafeFrame = y; + }else{ + goto walcheckpoint_out; + } } } - } + }while( xBusy && mxSafeFramehdr.mxFrame && xBusy(pBusyArg) ); if( pInfo->nBackfillnBackfill; @@ -1666,10 +1690,19 @@ static int walCheckpoint( /* Release the reader lock held while backfilling */ walUnlockExclusive(pWal, WAL_READ_LOCK(0), 1); - }else if( rc==SQLITE_BUSY ){ + + if( xBusy && rc==SQLITE_OK && pWal->hdr.mxFrame==mxSafeFrame ){ + assert( pWal->writeLock ); + rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(1), WAL_NREADER-1); + if( rc==SQLITE_OK ){ + walUnlockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1); + } + } + } + + if( rc==SQLITE_BUSY ){ /* Reset the return code so as not to report a checkpoint failure - ** just because active readers prevent any backfill. - */ + ** just because there are active readers. */ rc = SQLITE_OK; } @@ -1704,7 +1737,7 @@ int sqlite3WalClose( if( pWal->exclusiveMode==WAL_NORMAL_MODE ){ pWal->exclusiveMode = WAL_EXCLUSIVE_MODE; } - rc = sqlite3WalCheckpoint(pWal, sync_flags, nBuf, zBuf); + rc = sqlite3WalCheckpoint(pWal, 0, 0, sync_flags, nBuf, zBuf); if( rc==SQLITE_OK ){ isDelete = 1; } @@ -2619,9 +2652,14 @@ int sqlite3WalFrames( ** ** Obtain a CHECKPOINT lock and then backfill as much information as ** we can from WAL into the database. +** +** If parameter xBusy is not NULL, it is a pointer to a busy-handler +** callback. In this case this function runs a blocking checkpoint. */ int sqlite3WalCheckpoint( Wal *pWal, /* Wal connection */ + int (*xBusy)(void*), /* Function to call when busy */ + void *pBusyArg, /* Context argument for xBusyHandler */ int sync_flags, /* Flags to sync db file with (or 0) */ int nBuf, /* Size of temporary buffer */ u8 *zBuf /* Temporary buffer to use */ @@ -2630,6 +2668,7 @@ int sqlite3WalCheckpoint( int isChanged = 0; /* True if a new wal-index header is loaded */ assert( pWal->ckptLock==0 ); + assert( pWal->writeLock==0 ); WALTRACE(("WAL%p: checkpoint begins\n", pWal)); rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1); @@ -2641,10 +2680,21 @@ int sqlite3WalCheckpoint( } pWal->ckptLock = 1; + /* If this is a blocking-checkpoint, then obtain the write-lock as well + ** to prevent any writers from running while the checkpoint is underway. + ** This has to be done before the call to walIndexReadHdr() below. + */ + if( xBusy ){ + rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_WRITE_LOCK, 1); + if( rc==SQLITE_OK ) pWal->writeLock = 1; + } + /* Copy data from the log to the database file. */ - rc = walIndexReadHdr(pWal, &isChanged); if( rc==SQLITE_OK ){ - rc = walCheckpoint(pWal, sync_flags, nBuf, zBuf); + rc = walIndexReadHdr(pWal, &isChanged); + } + if( rc==SQLITE_OK ){ + rc = walCheckpoint(pWal, xBusy, pBusyArg, sync_flags, nBuf, zBuf); } if( isChanged ){ /* If a new wal-index header was loaded before the checkpoint was @@ -2657,6 +2707,7 @@ int sqlite3WalCheckpoint( } /* Release the locks. */ + sqlite3WalEndWriteTransaction(pWal); walUnlockExclusive(pWal, WAL_CKPT_LOCK, 1); pWal->ckptLock = 0; WALTRACE(("WAL%p: checkpoint %s\n", pWal, rc ? "failed" : "ok")); diff --git a/src/wal.h b/src/wal.h index 35f695c88a..e9c614fe85 100644 --- a/src/wal.h +++ b/src/wal.h @@ -32,7 +32,7 @@ # define sqlite3WalSavepoint(y,z) # define sqlite3WalSavepointUndo(y,z) 0 # define sqlite3WalFrames(u,v,w,x,y,z) 0 -# define sqlite3WalCheckpoint(u,v,w,x) 0 +# define sqlite3WalCheckpoint(u,v,w,x,y,z) 0 # define sqlite3WalCallback(z) 0 # define sqlite3WalExclusiveMode(y,z) 0 # define sqlite3WalHeapMemory(z) 0 @@ -86,6 +86,8 @@ int sqlite3WalFrames(Wal *pWal, int, PgHdr *, Pgno, int, int); /* Copy pages from the log to the database file */ int sqlite3WalCheckpoint( Wal *pWal, /* Write-ahead log connection */ + int (*xBusy)(void*), /* Function to call when busy */ + void *pBusyArg, /* Context argument for xBusyHandler */ int sync_flags, /* Flags to sync db file with (or 0) */ int nBuf, /* Size of buffer nBuf */ u8 *zBuf /* Temporary buffer to use */ diff --git a/test/wal5.test b/test/wal5.test new file mode 100644 index 0000000000..24d6353a62 --- /dev/null +++ b/test/wal5.test @@ -0,0 +1,118 @@ +# 2010 April 13 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. The +# focus of this file is testing the operation of "blocking-checkpoint" +# operations. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +source $testdir/lock_common.tcl +source $testdir/wal_common.tcl +ifcapable !wal {finish_test ; return } + +set testprefix wal5 + +do_multiclient_test tn { + + proc db_page_count {} { expr [file size test.db] / 1024 } + proc wal_page_count {} { wal_frame_count test.db-wal 1024 } + + set ::nBusyHandler 0 + set ::busy_handler_script "" + proc busyhandler {n} { + incr ::nBusyHandler + eval $::busy_handler_script + return 0 + } + + proc reopen_all {} { + code1 {db close} + code2 {db2 close} + code3 {db3 close} + code1 {sqlite3 db test.db} + code2 {sqlite3 db2 test.db} + code3 {sqlite3 db3 test.db} + sql1 { PRAGMA synchronous = NORMAL } + code1 { db busy busyhandler } + } + + do_test 1.$tn.1 { + reopen_all + sql1 { + PRAGMA page_size = 1024; + PRAGMA auto_vacuum = 0; + CREATE TABLE t1(x, y); + PRAGMA journal_mode = WAL; + INSERT INTO t1 VALUES(1, zeroblob(1200)); + INSERT INTO t1 VALUES(2, zeroblob(1200)); + INSERT INTO t1 VALUES(3, zeroblob(1200)); + } + expr [file size test.db] / 1024 + } {2} + + # Have connection 2 grab a read-lock on the current snapshot. + do_test 1.$tn.2 { sql2 { BEGIN; SELECT x FROM t1 } } {1 2 3} + + # Attempt a checkpoint. + do_test 1.$tn.3 { + sql1 { PRAGMA wal_checkpoint } + list [db_page_count] [wal_page_count] + } {5 9} + + # Write to the db again. The log cannot wrap because of the lock still + # held by connection 2. The busy-handler has not yet been invoked. + do_test 1.$tn.4 { + sql1 { INSERT INTO t1 VALUES(4, zeroblob(1200)) } + list [db_page_count] [wal_page_count] $::nBusyHandler + } {5 12 0} + + # Now do a blocking-checkpoint. Set the busy-handler up so that connection + # 2 releases its lock on the 6th invocation. The checkpointer should then + # proceed to checkpoint the entire log file. Next write should go to the + # start of the log file. + # + set ::busy_handler_script { if {$n==5} { sql2 COMMIT } } + do_test 1.$tn.5 { + sql1 { PRAGMA wal_blocking_checkpoint } + list [db_page_count] [wal_page_count] $::nBusyHandler + } {6 12 6} + do_test 1.$tn.6 { + set ::nBusyHandler 0 + sql1 { INSERT INTO t1 VALUES(5, zeroblob(1200)) } + list [db_page_count] [wal_page_count] $::nBusyHandler + } {6 12 0} + + do_test 1.$tn.7 { + reopen_all + list [db_page_count] [wal_page_count] $::nBusyHandler + } {7 0 0} + + do_test 1.$tn.8 { sql2 { BEGIN ; SELECT x FROM t1 } } {1 2 3 4 5} + do_test 1.$tn.9 { + sql1 { INSERT INTO t1 VALUES(6, zeroblob(1200)) } + list [db_page_count] [wal_page_count] $::nBusyHandler + } {7 5 0} + do_test 1.$tn.10 { sql3 { BEGIN ; SELECT x FROM t1 } } {1 2 3 4 5 6} + + set ::busy_handler_script { + if {$n==5} { sql2 COMMIT } + if {$n==6} { set ::db_file_size [db_page_count] } + if {$n==7} { sql3 COMMIT } + } + do_test 1.$tn.11 { + sql1 { PRAGMA wal_blocking_checkpoint } + list [db_page_count] [wal_page_count] $::nBusyHandler + } {10 5 8} + do_test 1.$tn.12 { set ::db_file_size } 10 +} + +finish_test diff --git a/test/wal_common.tcl b/test/wal_common.tcl index a5b165d5ec..917ad598f6 100644 --- a/test/wal_common.tcl +++ b/test/wal_common.tcl @@ -18,7 +18,9 @@ proc wal_file_size {nFrame pgsz} { } proc wal_frame_count {zFile pgsz} { + if {[file exists $zFile]==0} { return 0 } set f [file size $zFile] + if {$f < 32} { return 0 } expr {($f - 32) / ($pgsz+24)} }