1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-08-07 02:42:48 +03:00

Fixes for SQLITE_BUSY handling in blocking checkpoint code.

FossilOrigin-Name: 4c663a4dcc77e00558edd94f58410605b95db33a
This commit is contained in:
dan
2010-11-18 19:28:01 +00:00
parent d2892119c8
commit f2b8dd588d
6 changed files with 172 additions and 55 deletions

View File

@@ -1,5 +1,5 @@
C Merge\swith\slatest\strunk\sfix. C Fixes\sfor\sSQLITE_BUSY\shandling\sin\sblocking\scheckpoint\scode.
D 2010-11-18T16:59:24 D 2010-11-18T19:28:02
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in e7a59672eaeb04408d1fa8501618d7501a3c5e39 F Makefile.in e7a59672eaeb04408d1fa8501618d7501a3c5e39
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -141,7 +141,7 @@ F src/journal.c 552839e54d1bf76fb8f7abe51868b66acacf6a0e
F src/legacy.c a199d7683d60cef73089e892409113e69c23a99f F src/legacy.c a199d7683d60cef73089e892409113e69c23a99f
F src/lempar.c 7f026423f4d71d989e719a743f98a1cbd4e6d99e F src/lempar.c 7f026423f4d71d989e719a743f98a1cbd4e6d99e
F src/loadext.c 8af9fcc75708d60b88636ccba38b4a7b3c155c3e F src/loadext.c 8af9fcc75708d60b88636ccba38b4a7b3c155c3e
F src/main.c 91465f2658911ddb51be89e7b8ee01af8584308f F src/main.c 5927ac9ca30c46df68148ad68227329d1be41a21
F src/malloc.c 3d7284cd9346ab6e3945535761e68c23c6cf40ef F src/malloc.c 3d7284cd9346ab6e3945535761e68c23c6cf40ef
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
F src/mem1.c 00bd8265c81abb665c48fea1e0c234eb3b922206 F src/mem1.c 00bd8265c81abb665c48fea1e0c234eb3b922206
@@ -236,7 +236,7 @@ F src/vdbeblob.c e0ce3c54cc0c183af2ec67b63a289acf92251df4
F src/vdbemem.c 23723a12cd3ba7ab3099193094cbb2eb78956aa9 F src/vdbemem.c 23723a12cd3ba7ab3099193094cbb2eb78956aa9
F src/vdbetrace.c 864cef96919323482ebd9986f2132435115e9cc2 F src/vdbetrace.c 864cef96919323482ebd9986f2132435115e9cc2
F src/vtab.c b297e8fa656ab5e66244ab15680d68db0adbec30 F src/vtab.c b297e8fa656ab5e66244ab15680d68db0adbec30
F src/wal.c 8eca619a28a70a667c913e5927131250836377a2 F src/wal.c 23facfd0f148ac72729fe28bbf973fe0458757b6
F src/wal.h 7a5fbb00114b7f2cd40c7e1003d4c41ce9d26840 F src/wal.h 7a5fbb00114b7f2cd40c7e1003d4c41ce9d26840
F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f
F src/where.c fa22d45b2577c77146f2e894d58011d472d64103 F src/where.c fa22d45b2577c77146f2e894d58011d472d64103
@@ -824,11 +824,11 @@ F test/vtabE.test 7c4693638d7797ce2eda17af74292b97e705cc61
F test/vtab_alter.test 9e374885248f69e251bdaacf480b04a197f125e5 F test/vtab_alter.test 9e374885248f69e251bdaacf480b04a197f125e5
F test/vtab_err.test 0d4d8eb4def1d053ac7c5050df3024fd47a3fbd8 F test/vtab_err.test 0d4d8eb4def1d053ac7c5050df3024fd47a3fbd8
F test/vtab_shared.test 0eff9ce4f19facbe0a3e693f6c14b80711a4222d F test/vtab_shared.test 0eff9ce4f19facbe0a3e693f6c14b80711a4222d
F test/wal.test dea22218fd68c61fe206f53d508a0552894144b7 F test/wal.test f060cae4b2164c4375109a8f803873187234661d
F test/wal2.test 894d55dda774340fe7bebe239bed9b6130ff23d7 F test/wal2.test 894d55dda774340fe7bebe239bed9b6130ff23d7
F test/wal3.test 55529a3fbf0a04670558dbf0b06f04a2f3508db4 F test/wal3.test 55529a3fbf0a04670558dbf0b06f04a2f3508db4
F test/wal4.test 3404b048fa5e10605facaf70384e6d2943412e30 F test/wal4.test 3404b048fa5e10605facaf70384e6d2943412e30
F test/wal5.test e5330471fc284d9ae62a2a18c9dfd10b6605d8b6 F test/wal5.test 79963972107e4cabda4c4b44a64e89c3e9af463d
F test/wal_common.tcl a98f17fba96206122eff624db0ab13ec377be4fe F test/wal_common.tcl a98f17fba96206122eff624db0ab13ec377be4fe
F test/walbak.test 4df1c7369da0301caeb9a48fa45997fd592380e4 F test/walbak.test 4df1c7369da0301caeb9a48fa45997fd592380e4
F test/walbig.test e882bc1d014afffbfa2b6ba36e0f07d30a633ad0 F test/walbig.test e882bc1d014afffbfa2b6ba36e0f07d30a633ad0
@@ -888,7 +888,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
P e376480f0855c598c91529abacbd73e31d9f4713 0a95589f2166f9ce420e647b73e8c797fe8f4833 P a8910e89dee326d7788b29e77820eb1e114739ca
R 87db3bd5bf4a3735ef4309e5d5955901 R a15fbbfd076e96cb91858a5a2944b7af
U dan U dan
Z 9fccaf5d4045282a697f1275247ad8b5 Z 77692281a1477b941972542152e8834d

View File

@@ -1 +1 @@
a8910e89dee326d7788b29e77820eb1e114739ca 4c663a4dcc77e00558edd94f58410605b95db33a

View File

@@ -1413,16 +1413,23 @@ int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb){
int sqlite3Checkpoint(sqlite3 *db, int iDb, int eMode, int *pnLog, int *pnCkpt){ int sqlite3Checkpoint(sqlite3 *db, int iDb, int eMode, int *pnLog, int *pnCkpt){
int rc = SQLITE_OK; /* Return code */ int rc = SQLITE_OK; /* Return code */
int i; /* Used to iterate through attached dbs */ int i; /* Used to iterate through attached dbs */
int bBusy = 0; /* True if SQLITE_BUSY has been encountered */
assert( sqlite3_mutex_held(db->mutex) ); assert( sqlite3_mutex_held(db->mutex) );
for(i=0; i<db->nDb && rc==SQLITE_OK; i++){ for(i=0; i<db->nDb && rc==SQLITE_OK; i++){
if( i==iDb || iDb==SQLITE_MAX_ATTACHED ){ if( i==iDb || iDb==SQLITE_MAX_ATTACHED ){
rc = sqlite3BtreeCheckpoint(db->aDb[i].pBt, eMode, pnLog, pnCkpt); rc = sqlite3BtreeCheckpoint(db->aDb[i].pBt, eMode, pnLog, pnCkpt);
pnLog = 0;
pnCkpt = 0;
if( rc==SQLITE_BUSY ){
bBusy = 1;
rc = SQLITE_OK;
}
} }
} }
return rc; return (rc==SQLITE_OK && bBusy) ? SQLITE_BUSY : rc;
} }
#endif /* SQLITE_OMIT_WAL */ #endif /* SQLITE_OMIT_WAL */

View File

@@ -1543,6 +1543,14 @@ static int walBusyLock(
return rc; return rc;
} }
/*
** The cache of the wal-index header must be valid to call this function.
** Return the page-size in bytes used by the database.
*/
static int walPagesize(Wal *pWal){
return (pWal->hdr.szPage&0xfe00) + ((pWal->hdr.szPage&0x0001)<<16);
}
/* /*
** Copy as much content as we can from the WAL back into the database file ** 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. ** in response to an sqlite3_wal_checkpoint() request or the equivalent.
@@ -1577,10 +1585,9 @@ static int walBusyLock(
static int walCheckpoint( static int walCheckpoint(
Wal *pWal, /* Wal connection */ Wal *pWal, /* Wal connection */
int eMode, /* One of PASSIVE, FULL or RESTART */ int eMode, /* One of PASSIVE, FULL or RESTART */
int (*xBusy)(void*), /* Function to call when busy */ int (*xBusyCall)(void*), /* Function to call when busy */
void *pBusyArg, /* Context argument for xBusyHandler */ void *pBusyArg, /* Context argument for xBusyHandler */
int sync_flags, /* Flags for OsSync() (or 0) */ int sync_flags, /* Flags for OsSync() (or 0) */
int nBuf, /* Size of zBuf in bytes */
u8 *zBuf, /* Temporary buffer to use */ u8 *zBuf, /* Temporary buffer to use */
int *pnCkpt /* Total frames checkpointed */ int *pnCkpt /* Total frames checkpointed */
){ ){
@@ -1593,11 +1600,11 @@ static int walCheckpoint(
u32 mxPage; /* Max database page to write */ u32 mxPage; /* Max database page to write */
int i; /* Loop counter */ int i; /* Loop counter */
volatile WalCkptInfo *pInfo; /* The checkpoint status information */ volatile WalCkptInfo *pInfo; /* The checkpoint status information */
int (*xBusy)(void*) = 0; /* Function to call when waiting for locks */
szPage = (pWal->hdr.szPage&0xfe00) + ((pWal->hdr.szPage&0x0001)<<16); szPage = walPagesize(pWal);
testcase( szPage<=32768 ); testcase( szPage<=32768 );
testcase( szPage>=65536 ); testcase( szPage>=65536 );
if( pWal->hdr.mxFrame==0 ) return SQLITE_OK;
/* Allocate the iterator */ /* Allocate the iterator */
rc = walIteratorInit(pWal, &pIter); rc = walIteratorInit(pWal, &pIter);
@@ -1606,40 +1613,33 @@ static int walCheckpoint(
} }
assert( pIter ); assert( pIter );
/*** TODO: Move this test out to the caller. Make it an assert() here ***/
if( szPage!=nBuf ){
rc = SQLITE_CORRUPT_BKPT;
goto walcheckpoint_out;
}
pInfo = walCkptInfo(pWal); pInfo = walCkptInfo(pWal);
mxPage = pWal->hdr.nPage; mxPage = pWal->hdr.nPage;
if( pnCkpt ) *pnCkpt = pInfo->nBackfill; if( pnCkpt ) *pnCkpt = pInfo->nBackfill;
if( eMode!=SQLITE_CHECKPOINT_PASSIVE ) xBusy = xBusyCall;
/* Compute in mxSafeFrame the index of the last frame of the WAL that is /* Compute in mxSafeFrame the index of the last frame of the WAL that is
** safe to write into the database. Frames beyond mxSafeFrame might ** safe to write into the database. Frames beyond mxSafeFrame might
** overwrite database pages that are in use by active readers and thus ** overwrite database pages that are in use by active readers and thus
** cannot be backfilled from the WAL. ** cannot be backfilled from the WAL.
*/ */
do {
mxSafeFrame = pWal->hdr.mxFrame; mxSafeFrame = pWal->hdr.mxFrame;
for(i=1; i<WAL_NREADER; i++){ for(i=1; i<WAL_NREADER; i++){
u32 y = pInfo->aReadMark[i]; u32 y = pInfo->aReadMark[i];
if( mxSafeFrame>=y ){ if( mxSafeFrame>y ){
assert( y<=pWal->hdr.mxFrame ); assert( y<=pWal->hdr.mxFrame );
rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1); rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(i), 1);
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
pInfo->aReadMark[i] = READMARK_NOT_USED; pInfo->aReadMark[i] = READMARK_NOT_USED;
walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1); walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1);
}else if( rc==SQLITE_BUSY ){ }else if( rc==SQLITE_BUSY ){
mxSafeFrame = y; mxSafeFrame = y;
xBusy = 0;
}else{ }else{
goto walcheckpoint_out; goto walcheckpoint_out;
} }
} }
} }
}while( eMode!=SQLITE_CHECKPOINT_PASSIVE
&& xBusy && mxSafeFrame<pWal->hdr.mxFrame && xBusy(pBusyArg) );
if( pInfo->nBackfill<mxSafeFrame if( pInfo->nBackfill<mxSafeFrame
&& (rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(0), 1))==SQLITE_OK && (rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(0), 1))==SQLITE_OK
@@ -1704,16 +1704,23 @@ static int walCheckpoint(
rc = SQLITE_OK; rc = SQLITE_OK;
} }
if( rc==SQLITE_OK /* If this is an SQLITE_CHECKPOINT_RESTART operation, and the entire wal
&& eMode==SQLITE_CHECKPOINT_RESTART ** file has been copied into the database file, then block until all
&& pWal->hdr.mxFrame==mxSafeFrame ** readers have finished using the wal file. This ensures that the next
){ ** process to write to the database restarts the wal file.
*/
if( rc==SQLITE_OK && eMode!=SQLITE_CHECKPOINT_PASSIVE ){
assert( pWal->writeLock ); assert( pWal->writeLock );
if( pInfo->nBackfill<pWal->hdr.mxFrame ){
rc = SQLITE_BUSY;
}else if( eMode==SQLITE_CHECKPOINT_RESTART ){
assert( mxSafeFrame==pWal->hdr.mxFrame );
rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(1), WAL_NREADER-1); rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(1), WAL_NREADER-1);
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
walUnlockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1); walUnlockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1);
} }
} }
}
walcheckpoint_out: walcheckpoint_out:
walIteratorFree(pIter); walIteratorFree(pIter);
@@ -2680,6 +2687,7 @@ int sqlite3WalCheckpoint(
){ ){
int rc; /* Return code */ int rc; /* Return code */
int isChanged = 0; /* True if a new wal-index header is loaded */ int isChanged = 0; /* True if a new wal-index header is loaded */
int eMode2 = eMode; /* Mode to pass to walCheckpoint() */
assert( pWal->ckptLock==0 ); assert( pWal->ckptLock==0 );
assert( pWal->writeLock==0 ); assert( pWal->writeLock==0 );
@@ -2697,21 +2705,37 @@ int sqlite3WalCheckpoint(
/* If this is a blocking-checkpoint, then obtain the write-lock as well /* If this is a blocking-checkpoint, then obtain the write-lock as well
** to prevent any writers from running while the checkpoint is underway. ** to prevent any writers from running while the checkpoint is underway.
** This has to be done before the call to walIndexReadHdr() below. ** This has to be done before the call to walIndexReadHdr() below.
**
** If the writer lock cannot be obtained, then a passive checkpoint is
** run instead. Since the checkpointer is not holding the writer lock,
** there is no point in blocking waiting for any readers. Assuming no
** other error occurs, this function will return SQLITE_BUSY to the caller.
*/ */
if( eMode!=SQLITE_CHECKPOINT_PASSIVE ){ if( eMode!=SQLITE_CHECKPOINT_PASSIVE ){
rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_WRITE_LOCK, 1); rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_WRITE_LOCK, 1);
if( rc==SQLITE_OK ) pWal->writeLock = 1; if( rc==SQLITE_OK ){
pWal->writeLock = 1;
}else if( rc==SQLITE_BUSY ){
eMode2 = SQLITE_CHECKPOINT_PASSIVE;
rc = SQLITE_OK;
}
} }
/* Copy data from the log to the database file. */ /* Read the wal-index header. */
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
rc = walIndexReadHdr(pWal, &isChanged); rc = walIndexReadHdr(pWal, &isChanged);
} }
if( rc==SQLITE_OK ){
/* Copy data from the log to the database file. */
if( rc==SQLITE_OK && pWal->hdr.mxFrame ){
if( walPagesize(pWal)!=nBuf ){
rc = SQLITE_CORRUPT_BKPT;
}else{
if( pnLog ) *pnLog = (int)pWal->hdr.mxFrame; if( pnLog ) *pnLog = (int)pWal->hdr.mxFrame;
rc = walCheckpoint( rc = walCheckpoint(pWal, eMode2, xBusy, pBusyArg, sync_flags,zBuf,pnCkpt);
pWal, eMode, xBusy, pBusyArg, sync_flags, nBuf, zBuf, pnCkpt);
} }
}
if( isChanged ){ if( isChanged ){
/* If a new wal-index header was loaded before the checkpoint was /* If a new wal-index header was loaded before the checkpoint was
** performed, then the pager-cache associated with pWal is now ** performed, then the pager-cache associated with pWal is now
@@ -2727,7 +2751,7 @@ int sqlite3WalCheckpoint(
walUnlockExclusive(pWal, WAL_CKPT_LOCK, 1); walUnlockExclusive(pWal, WAL_CKPT_LOCK, 1);
pWal->ckptLock = 0; pWal->ckptLock = 0;
WALTRACE(("WAL%p: checkpoint %s\n", pWal, rc ? "failed" : "ok")); WALTRACE(("WAL%p: checkpoint %s\n", pWal, rc ? "failed" : "ok"));
return rc; return (rc==SQLITE_OK && eMode!=eMode2 ? SQLITE_BUSY : rc);
} }
/* Return the value to pass to a sqlite3_wal_hook callback, the /* Return the value to pass to a sqlite3_wal_hook callback, the

View File

@@ -845,6 +845,7 @@ do_test wal-13.1.2 {
sqlite3 db test.db sqlite3 db test.db
execsql { SELECT * FROM t2 } execsql { SELECT * FROM t2 }
} {B 2} } {B 2}
breakpoint
do_test wal-13.1.3 { do_test wal-13.1.3 {
db close db close
file exists test.db-wal file exists test.db-wal
@@ -1029,7 +1030,7 @@ catch { db close }
foreach {tn ckpt_cmd ckpt_res ckpt_main ckpt_aux} { foreach {tn ckpt_cmd ckpt_res ckpt_main ckpt_aux} {
1 {sqlite3_wal_checkpoint db} SQLITE_OK 1 1 1 {sqlite3_wal_checkpoint db} SQLITE_OK 1 1
2 {sqlite3_wal_checkpoint db ""} SQLITE_OK 1 1 2 {sqlite3_wal_checkpoint db ""} SQLITE_OK 1 1
3 {db eval "PRAGMA wal_checkpoint"} {0 16 16} 1 1 3 {db eval "PRAGMA wal_checkpoint"} {0 10 10} 1 1
4 {sqlite3_wal_checkpoint db main} SQLITE_OK 1 0 4 {sqlite3_wal_checkpoint db main} SQLITE_OK 1 0
5 {sqlite3_wal_checkpoint db aux} SQLITE_OK 0 1 5 {sqlite3_wal_checkpoint db aux} SQLITE_OK 0 1

View File

@@ -21,10 +21,12 @@ ifcapable !wal {finish_test ; return }
set testprefix wal5 set testprefix wal5
proc db_page_count {{file test.db}} { expr [file size $file] / 1024 }
proc wal_page_count {{file test.db}} { wal_frame_count ${file}-wal 1024 }
do_multiclient_test tn { 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 ::nBusyHandler 0
set ::busy_handler_script "" set ::busy_handler_script ""
@@ -115,4 +117,87 @@ do_multiclient_test tn {
do_test 1.$tn.12 { set ::db_file_size } 10 do_test 1.$tn.12 { set ::db_file_size } 10
} }
#-------------------------------------------------------------------------
# This block of tests explores checkpoint operations on more than one
# database file.
#
proc setup_and_attach_aux {} {
sql1 { ATTACH 'test.db2' AS aux }
sql2 { ATTACH 'test.db2' AS aux }
sql3 { ATTACH 'test.db2' AS aux }
sql1 {
PRAGMA main.page_size=1024; PRAGMA main.journal_mode=WAL;
PRAGMA aux.page_size=1024; PRAGMA aux.journal_mode=WAL;
}
}
proc file_page_counts {} {
list [db_page_count test.db ] \
[wal_page_count test.db ] \
[db_page_count test.db2] \
[wal_page_count test.db2]
}
do_multiclient_test tn {
setup_and_attach_aux
do_test 2.1.$tn.1 {
sql1 {
CREATE TABLE t1(a, b);
INSERT INTO t1 VALUES(1, 2);
CREATE TABLE aux.t2(a, b);
INSERT INTO t2 VALUES(1, 2);
}
} {}
do_test 2.2.$tn.2 { file_page_counts } {1 5 1 5}
do_test 2.1.$tn.3 { sql1 { PRAGMA wal_checkpoint } } {0 5 5}
do_test 2.1.$tn.4 { file_page_counts } {2 5 2 5}
}
do_multiclient_test tn {
setup_and_attach_aux
do_test 2.2.$tn.1 {
execsql {
CREATE TABLE t1(a, b);
INSERT INTO t1 VALUES(1, 2);
CREATE TABLE aux.t2(a, b);
INSERT INTO t2 VALUES(1, 2);
INSERT INTO t2 VALUES(3, 4);
}
} {}
do_test 2.2.$tn.2 { file_page_counts } {1 5 1 7}
do_test 2.2.$tn.3 { sql2 { BEGIN; SELECT * FROM t1 } } {1 2}
do_test 2.2.$tn.4 { sql1 { PRAGMA wal_checkpoint = RESTART } } {1 5 5}
do_test 2.2.$tn.5 { file_page_counts } {2 5 2 7}
}
do_multiclient_test tn {
setup_and_attach_aux
do_test 2.3.$tn.1 {
execsql {
CREATE TABLE t1(a, b);
INSERT INTO t1 VALUES(1, 2);
CREATE TABLE aux.t2(a, b);
INSERT INTO t2 VALUES(1, 2);
}
} {}
do_test 2.3.$tn.2 { file_page_counts } {1 5 1 5}
do_test 2.3.$tn.3 { sql2 { BEGIN; SELECT * FROM t1 } } {1 2}
do_test 2.3.$tn.4 { sql1 { INSERT INTO t1 VALUES(3, 4) } } {}
do_test 2.3.$tn.5 { sql1 { INSERT INTO t2 VALUES(3, 4) } } {}
do_test 2.3.$tn.6 { file_page_counts } {1 7 1 7}
do_test 2.3.$tn.7 { sql1 { PRAGMA wal_checkpoint = FULL } } {1 7 5}
do_test 2.3.$tn.8 { file_page_counts } {1 7 2 7}
}
finish_test finish_test