From: dan Date: Mon, 7 Feb 2011 15:12:12 +0000 (+0000) Subject: Change blocking-checkpoint tests so that they run once using "PRAGMA wal_checkpoint... X-Git-Tag: version-3.7.6~161 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=9c5e3680dfa359da2cf77202b561bd7e7df1c628;p=thirdparty%2Fsqlite.git Change blocking-checkpoint tests so that they run once using "PRAGMA wal_checkpoint" and once using calls to sqlite3_wal_checkpoint_v2(). Also fix edge cases surrounding the output variables set by wal_checkpoint_v2(). FossilOrigin-Name: 5a4b6652cf3780ffed6fe0fe669e8090b0b71e81 --- diff --git a/manifest b/manifest index 89e3b7b07a..727f42e185 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Ensure\sfts4aux\scan\shandle\sa\stable\sname\sin\ssingle\sor\sdouble\squotes\sas\sa\sconstructor\sargument. -D 2011-02-05T15:47:12.471 +C Change\sblocking-checkpoint\stests\sso\sthat\sthey\srun\sonce\susing\s"PRAGMA\swal_checkpoint"\sand\sonce\susing\scalls\sto\ssqlite3_wal_checkpoint_v2().\sAlso\sfix\sedge\scases\ssurrounding\sthe\soutput\svariables\sset\sby\swal_checkpoint_v2(). +D 2011-02-07T15:12:12.557 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in de6498556d536ae60bb8bb10e8c1ba011448658c F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -142,7 +142,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 1b04ef67eb03d026c8cc2d438c61635163153c24 +F src/main.c bc1c822dafa9a6c6c43179c0cbd3f1ce686a84c6 F src/malloc.c 92d59a007d7a42857d4e9454aa25b6b703286be1 F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 F src/mem1.c 00bd8265c81abb665c48fea1e0c234eb3b922206 @@ -184,7 +184,7 @@ F src/sqliteLimit.h a17dcd3fb775d63b64a43a55c54cb282f9726f44 F src/status.c 4997380fbb915426fef9e500b4872e79c99267fc F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e F src/tclsqlite.c 549859dc2c143f3deb6a92636a2d27973652c164 -F src/test1.c 771407a49ae199241f0efb7055634e4a1899c026 +F src/test1.c ddbfff546e22e5c31204c973fdd805a4eda3e208 F src/test2.c 80d323d11e909cf0eb1b6fbb4ac22276483bcf31 F src/test3.c 056093cfef69ff4227a6bdb9108564dc7f45e4bc F src/test4.c 0528360b5025688002a5feb6be906ddce52eaaee @@ -238,7 +238,7 @@ F src/vdbeblob.c 18955f0ee6b133cd08e1592010cb9a6b11e9984c F src/vdbemem.c c011228c6fb1b5df924e4584765b16bde863c9c6 F src/vdbetrace.c 3ba13bc32bdf16d2bdea523245fd16736bed67b5 F src/vtab.c b297e8fa656ab5e66244ab15680d68db0adbec30 -F src/wal.c 8704a563b37c0c48b6a65d49da5d5656568abfc6 +F src/wal.c aca10a60655e103fc8630a75345000f43c6d47ca F src/wal.h 7a5fbb00114b7f2cd40c7e1003d4c41ce9d26840 F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f F src/where.c f4915ac03e5e42c8416b35ca3ba34af841c00d12 @@ -662,7 +662,7 @@ F test/stmt.test 25d64e3dbf9a3ce89558667d7f39d966fe2a71b9 F test/subquery.test b524f57c9574b2c0347045b4510ef795d4686796 F test/subselect.test d24fd8757daf97dafd2e889c73ea4c4272dcf4e4 F test/substr.test 18f57c4ca8a598805c4d64e304c418734d843c1a -F test/superlock.test f66a8f26dcd390c115bb732a2ea4638ab8204b13 +F test/superlock.test 5d7a4954b0059c903f82c7b67867bc5451a7c082 F test/sync.test ded6b39d8d8ca3c0c5518516c6371b3316d3e3a3 F test/table.test 04ba066432430657712d167ebf28080fe878d305 F test/tableapi.test 7262a8cbaa9965d429f1cbd2747edc185fa56516 @@ -845,7 +845,7 @@ F test/wal.test f060cae4b2164c4375109a8f803873187234661d F test/wal2.test 57a218446654ed3e3592c925762633c1d1e85636 F test/wal3.test ec87d9dd9e9cebabed4024064e8ff531d336ead2 F test/wal4.test 3404b048fa5e10605facaf70384e6d2943412e30 -F test/wal5.test 1f99651d856c8b9e1376781c981d1b903e93a478 +F test/wal5.test 3fef990d256cd9e95e9ad97e5dfdf3f150743fce F test/wal6.test 07aa31ca8892d0527f2c5c5a9a2a87aa421dfaa8 F test/wal_common.tcl a98f17fba96206122eff624db0ab13ec377be4fe F test/walbak.test 4df1c7369da0301caeb9a48fa45997fd592380e4 @@ -906,7 +906,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f -P dc511e60a65232a7087e12ff40b63506cf37a634 -R 8b6bef7500d37a03d9cad23f74319954 +P 929d62e496bb36a3ee0e19ec4609329d79aaeddc +R 5272b64137511ea0a87063bfd04f9af5 U dan -Z 659706e533fac6825c1f6da67c2f77f1 +Z 8b5f17a482d9dd489ee7270bd10b0d2b diff --git a/manifest.uuid b/manifest.uuid index fa38ef0c73..d9e6c00cd0 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -929d62e496bb36a3ee0e19ec4609329d79aaeddc \ No newline at end of file +5a4b6652cf3780ffed6fe0fe669e8090b0b71e81 \ No newline at end of file diff --git a/src/main.c b/src/main.c index 9f7d4b1fdf..b202ac9e56 100644 --- a/src/main.c +++ b/src/main.c @@ -1356,6 +1356,10 @@ int sqlite3_wal_checkpoint_v2( int rc; /* Return code */ int iDb = SQLITE_MAX_ATTACHED; /* sqlite3.aDb[] index of db to checkpoint */ + /* Initialize the output variables to -1 in case an error occurs. */ + if( pnLog ) *pnLog = -1; + if( pnCkpt ) *pnCkpt = -1; + if( eMode!=SQLITE_CHECKPOINT_PASSIVE && eMode!=SQLITE_CHECKPOINT_FULL && eMode!=SQLITE_CHECKPOINT_RESTART @@ -1416,6 +1420,8 @@ int sqlite3Checkpoint(sqlite3 *db, int iDb, int eMode, int *pnLog, int *pnCkpt){ int bBusy = 0; /* True if SQLITE_BUSY has been encountered */ assert( sqlite3_mutex_held(db->mutex) ); + assert( !pnLog || *pnLog==-1 ); + assert( !pnCkpt || *pnCkpt==-1 ); for(i=0; inDb && rc==SQLITE_OK; i++){ if( i==iDb || iDb==SQLITE_MAX_ATTACHED ){ diff --git a/src/test1.c b/src/test1.c index cf7e06b9c7..7f09b29294 100644 --- a/src/test1.c +++ b/src/test1.c @@ -5186,6 +5186,73 @@ static int test_wal_checkpoint( return TCL_OK; } +/* +** tclcmd: sqlite3_wal_checkpoint_v2 db MODE ?NAME? +** +** This command calls the wal_checkpoint_v2() function with the specified +** mode argument (passive, full or restart). If present, the database name +** NAME is passed as the second argument to wal_checkpoint_v2(). If it the +** NAME argument is not present, a NULL pointer is passed instead. +** +** If wal_checkpoint_v2() returns any value other than SQLITE_BUSY or +** SQLITE_OK, then this command returns TCL_ERROR. The Tcl result is set +** to the error message obtained from sqlite3_errmsg(). +** +** Otherwise, this command returns a list of three integers. The first integer +** is 1 if SQLITE_BUSY was returned, or 0 otherwise. The following two integers +** are the values returned via the output paramaters by wal_checkpoint_v2() - +** the number of frames in the log and the number of frames in the log +** that have been checkpointed. +*/ +static int test_wal_checkpoint_v2( + ClientData clientData, /* Unused */ + Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ + int objc, /* Number of arguments */ + Tcl_Obj *CONST objv[] /* Command arguments */ +){ + char *zDb = 0; + sqlite3 *db; + int rc; + + int eMode; + int nLog = -555; + int nCkpt = -555; + Tcl_Obj *pRet; + + const char * aMode[] = { "passive", "full", "restart", 0 }; + assert( SQLITE_CHECKPOINT_PASSIVE==0 ); + assert( SQLITE_CHECKPOINT_FULL==1 ); + assert( SQLITE_CHECKPOINT_RESTART==2 ); + + if( objc!=3 && objc!=4 ){ + Tcl_WrongNumArgs(interp, 1, objv, "DB MODE ?NAME?"); + return TCL_ERROR; + } + + if( objc==4 ){ + zDb = Tcl_GetString(objv[3]); + } + if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) + || Tcl_GetIndexFromObj(interp, objv[2], aMode, "mode", 0, &eMode) + ){ + return TCL_ERROR; + } + + rc = sqlite3_wal_checkpoint_v2(db, zDb, eMode, &nLog, &nCkpt); + if( rc!=SQLITE_OK && rc!=SQLITE_BUSY ){ + Tcl_SetResult(interp, (char *)sqlite3_errmsg(db), TCL_VOLATILE); + return TCL_ERROR; + } + + pRet = Tcl_NewObj(); + Tcl_ListObjAppendElement(interp, pRet, Tcl_NewIntObj(rc==SQLITE_BUSY?1:0)); + Tcl_ListObjAppendElement(interp, pRet, Tcl_NewIntObj(nLog)); + Tcl_ListObjAppendElement(interp, pRet, Tcl_NewIntObj(nCkpt)); + Tcl_SetObjResult(interp, pRet); + + return TCL_OK; +} + /* ** tclcmd: test_sqlite3_log ?SCRIPT? */ @@ -5572,6 +5639,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){ { "sqlite3_unlock_notify", test_unlock_notify, 0 }, #endif { "sqlite3_wal_checkpoint", test_wal_checkpoint, 0 }, + { "sqlite3_wal_checkpoint_v2",test_wal_checkpoint_v2, 0 }, { "test_sqlite3_log", test_sqlite3_log, 0 }, { "print_explain_query_plan", test_print_eqp, 0 }, }; diff --git a/src/wal.c b/src/wal.c index 64c966da97..34fb6cbbcd 100644 --- a/src/wal.c +++ b/src/wal.c @@ -1623,8 +1623,7 @@ static int walCheckpoint( int (*xBusyCall)(void*), /* Function to call when busy */ void *pBusyArg, /* Context argument for xBusyHandler */ int sync_flags, /* Flags for OsSync() (or 0) */ - u8 *zBuf, /* Temporary buffer to use */ - int *pnCkpt /* Total frames checkpointed */ + u8 *zBuf /* Temporary buffer to use */ ){ int rc; /* Return code */ int szPage; /* Database page-size */ @@ -1641,7 +1640,6 @@ static int walCheckpoint( testcase( szPage<=32768 ); testcase( szPage>=65536 ); pInfo = walCkptInfo(pWal); - if( pnCkpt ) *pnCkpt = pInfo->nBackfill; if( pInfo->nBackfill>=pWal->hdr.mxFrame ) return SQLITE_OK; /* Allocate the iterator */ @@ -1727,7 +1725,6 @@ static int walCheckpoint( } if( rc==SQLITE_OK ){ pInfo->nBackfill = mxSafeFrame; - if( pnCkpt ) *pnCkpt = mxSafeFrame; } } @@ -2764,12 +2761,17 @@ int sqlite3WalCheckpoint( } /* Copy data from the log to the database file. */ - if( rc==SQLITE_OK && pWal->hdr.mxFrame ){ - if( walPagesize(pWal)!=nBuf ){ + if( rc==SQLITE_OK ){ + if( pWal->hdr.mxFrame && walPagesize(pWal)!=nBuf ){ rc = SQLITE_CORRUPT_BKPT; }else{ + rc = walCheckpoint(pWal, eMode2, xBusy, pBusyArg, sync_flags, zBuf); + } + + /* If no error occurred, set the output variables. */ + if( rc==SQLITE_OK || rc==SQLITE_BUSY ){ if( pnLog ) *pnLog = (int)pWal->hdr.mxFrame; - rc = walCheckpoint(pWal, eMode2, xBusy, pBusyArg, sync_flags,zBuf,pnCkpt); + if( pnCkpt ) *pnCkpt = (int)(walCkptInfo(pWal)->nBackfill); } } diff --git a/test/superlock.test b/test/superlock.test index 01188203d7..b12863f7ff 100644 --- a/test/superlock.test +++ b/test/superlock.test @@ -220,7 +220,7 @@ db_swap test.db2 test.db do_catchsql_test 6.4 { SELECT * FROM t1 } {0 {1 2 3 4}} do_catchsql_test 6.5 { SELECT * FROM t2 } {1 {no such table: t2}} -do_execsql_test 6.6 { PRAGMA wal_checkpoint } {0 -1 -1} +do_execsql_test 6.6 { PRAGMA wal_checkpoint } {0 0 0} db_swap test.db2 test.db do_catchsql_test 6.7 { SELECT * FROM t1 } {1 {no such table: t1}} diff --git a/test/wal5.test b/test/wal5.test index 2ea4805795..d571dae0d1 100644 --- a/test/wal5.test +++ b/test/wal5.test @@ -25,235 +25,316 @@ 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 { +# A checkpoint may be requested either using the C API or by executing +# an SQL PRAGMA command. To test both methods, all tests in this file are +# run twice - once using each method to request checkpoints. +# +foreach {testprefix do_wal_checkpoint} { + wal5-pragma { + proc do_wal_checkpoint { dbhandle args } { + array set a $args + foreach key [array names a] { + if {[lsearch {-mode -db} $key]<0} { error "unknown switch: $key" } + } - set ::nBusyHandler 0 - set ::busy_handler_script "" - proc busyhandler {n} { - incr ::nBusyHandler - eval $::busy_handler_script - return 0 - } + set sql "PRAGMA " + if {[info exists a(-db)]} { append sql "$a(-db)." } + append sql "wal_checkpoint" + if {[info exists a(-mode)]} { append sql " = $a(-mode)" } - 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 } + uplevel [list $dbhandle eval $sql] + } } - 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)); + wal5-capi { + proc do_wal_checkpoint { dbhandle args } { + set a(-mode) passive + array set a $args + foreach key [array names a] { + if {[lsearch {-mode -db} $key]<0} { error "unknown switch: $key" } + } + + if {$a(-mode)!="restart" && $a(-mode)!="full"} { set a(-mode) passive } + + set cmd [list sqlite3_wal_checkpoint_v2 $dbhandle $a(-mode)] + if {[info exists a(-db)]} { lappend sql $a(-db) } + + uplevel $cmd } - 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_checkpoint = RESTART } - 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_checkpoint = RESTART } - list [db_page_count] [wal_page_count] $::nBusyHandler - } {10 5 8} - do_test 1.$tn.12 { set ::db_file_size } 10 -} +} { + eval $do_wal_checkpoint -#------------------------------------------------------------------------- -# 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; - } -} + do_multiclient_test tn { -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] -} + set ::nBusyHandler 0 + set ::busy_handler_script "" + proc busyhandler {n} { + incr ::nBusyHandler + eval $::busy_handler_script + return 0 + } -# Test that executing "PRAGMA wal_checkpoint" checkpoints all attached -# databases, not just the main db. -# -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); + 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 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 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 { + code1 { do_wal_checkpoint db } + 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 { + code1 { do_wal_checkpoint db -mode restart } + 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 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_test 1.$tn.11 { + code1 { do_wal_checkpoint db -mode restart } + list [db_page_count] [wal_page_count] $::nBusyHandler + } {10 5 8} + do_test 1.$tn.12 { set ::db_file_size } 10 + } -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); + #------------------------------------------------------------------------- + # 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; } - } {} - 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} -} + } -# Check that checkpoints block on the correct locks. And respond correctly -# if they cannot obtain those locks. There are three locks that a checkpoint -# may block on (in the following order): -# -# 1. The writer lock: FULL and RESTART checkpoints block until any writer -# process releases its lock. -# -# 2. Readers using part of the log file. FULL and RESTART checkpoints block -# until readers using part (but not all) of the log file have finished. -# -# 3. Readers using any of the log file. After copying data into the -# database file, RESTART checkpoints block until readers using any part -# of the log file have finished. -# -# This test case involves running a checkpoint while there exist other -# processes holding all three types of locks. -# -foreach {tn1 checkpoint busy_on ckpt_expected expected} { - 1 PASSIVE - {0 5 5} - - 2 TYPO - {0 5 5} - + 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] + } - 3 FULL - {0 7 7} 2 - 4 FULL 1 {1 5 5} 1 - 5 FULL 2 {1 7 5} 2 - 6 FULL 3 {0 7 7} 2 + # Test that executing "PRAGMA wal_checkpoint" checkpoints all attached + # databases, not just the main db. In capi mode, check that this is + # true if a NULL pointer is passed to wal_checkpoint_v2() in place of a + # database name. + 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 { code1 { do_wal_checkpoint db } } {0 5 5} + do_test 2.1.$tn.4 { file_page_counts } {2 5 2 5} + } - 7 RESTART - {0 7 7} 3 - 8 RESTART 1 {1 5 5} 1 - 9 RESTART 2 {1 7 5} 2 - 10 RESTART 3 {1 7 7} 3 + 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 { code1 { do_wal_checkpoint db -mode 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 - - proc busyhandler {x} { - set ::max_busyhandler $x - if {$::busy_on!="-" && $x==$::busy_on} { return 1 } - switch -- $x { - 1 { sql2 "COMMIT ; BEGIN ; SELECT * FROM t1" } - 2 { sql3 "COMMIT" } - 3 { sql2 "COMMIT" } + 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); } - return 0 + } {} + 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 { code1 { do_wal_checkpoint db -mode full } } {1 7 5} + do_test 2.3.$tn.8 { file_page_counts } {1 7 2 7} + } + + # Check that checkpoints block on the correct locks. And respond correctly + # if they cannot obtain those locks. There are three locks that a checkpoint + # may block on (in the following order): + # + # 1. The writer lock: FULL and RESTART checkpoints block until any writer + # process releases its lock. + # + # 2. Readers using part of the log file. FULL and RESTART checkpoints block + # until readers using part (but not all) of the log file have finished. + # + # 3. Readers using any of the log file. After copying data into the + # database file, RESTART checkpoints block until readers using any part + # of the log file have finished. + # + # This test case involves running a checkpoint while there exist other + # processes holding all three types of locks. + # + foreach {tn1 checkpoint busy_on ckpt_expected expected} { + 1 PASSIVE - {0 5 5} - + 2 TYPO - {0 5 5} - + + 3 FULL - {0 7 7} 2 + 4 FULL 1 {1 5 5} 1 + 5 FULL 2 {1 7 5} 2 + 6 FULL 3 {0 7 7} 2 + + 7 RESTART - {0 7 7} 3 + 8 RESTART 1 {1 5 5} 1 + 9 RESTART 2 {1 7 5} 2 + 10 RESTART 3 {1 7 7} 3 + + } { + do_multiclient_test tn { + setup_and_attach_aux + + proc busyhandler {x} { + set ::max_busyhandler $x + if {$::busy_on!="-" && $x==$::busy_on} { return 1 } + switch -- $x { + 1 { sql2 "COMMIT ; BEGIN ; SELECT * FROM t1" } + 2 { sql3 "COMMIT" } + 3 { sql2 "COMMIT" } + } + return 0 + } + set ::max_busyhandler - + + do_test 2.4.$tn1.$tn.1 { + sql1 { + CREATE TABLE t1(a, b); + INSERT INTO t1 VALUES(1, 2); + } + sql2 { BEGIN; INSERT INTO t1 VALUES(3, 4) } + sql3 { BEGIN; SELECT * FROM t1 } + } {1 2} + + do_test 2.4.$tn1.$tn.2 { + code1 { db busy busyhandler } + code1 { do_wal_checkpoint db -mode [string tolower $checkpoint] } + } $ckpt_expected + do_test 2.4.$tn1.$tn.3 { set ::max_busyhandler } $expected } - set ::max_busyhandler - - - do_test 2.4.$tn1.$tn.1 { + } + + + do_multiclient_test tn { + + code1 $do_wal_checkpoint + code2 $do_wal_checkpoint + code3 $do_wal_checkpoint + + do_test 3.$tn.1 { sql1 { - CREATE TABLE t1(a, b); - INSERT INTO t1 VALUES(1, 2); + PRAGMA journal_mode = WAL; + PRAGMA synchronous = normal; + CREATE TABLE t1(x, y); } - sql2 { BEGIN; INSERT INTO t1 VALUES(3, 4) } - sql3 { BEGIN; SELECT * FROM t1 } - } {1 2} - - do_test 2.4.$tn1.$tn.2 { - code1 { db busy busyhandler } - sql1 "PRAGMA wal_checkpoint = $checkpoint" - } $ckpt_expected - do_test 2.4.$tn1.$tn.3 { set ::max_busyhandler } $expected + + sql2 { PRAGMA journal_mode } + sql3 { PRAGMA journal_mode } + } {wal} + + do_test 3.$tn.2 { code2 { do_wal_checkpoint db2 } } {0 2 2} + + do_test 3.$tn.3 { code2 { do_wal_checkpoint db2 } } {0 2 2} + + do_test 3.$tn.4 { code3 { do_wal_checkpoint db3 } } {0 2 2} + + code1 {db close} + code2 {db2 close} + code3 {db3 close} + + code1 {sqlite3 db test.db} + code2 {sqlite3 db2 test.db} + code3 {sqlite3 db3 test.db} + + do_test 3.$tn.5 { sql3 { PRAGMA journal_mode } } {wal} + + do_test 3.$tn.6 { code3 { do_wal_checkpoint db3 } } {0 0 0} } }