diff --git a/manifest b/manifest index faab67f851..1f3fc5b551 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sexperimental\slocking\sscheme. -D 2010-04-13T19:27:31 +C Fixes\sfor\slocking\sissues\sin\sWAL\smode. +D 2010-04-14T11:23:30 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0 F Makefile.in 4f2f967b7e58a35bb74fb7ec8ae90e0f4ca7868b F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654 @@ -131,7 +131,7 @@ F src/journal.c b0ea6b70b532961118ab70301c00a33089f9315c F src/legacy.c a199d7683d60cef73089e892409113e69c23a99f F src/lempar.c 7f026423f4d71d989e719a743f98a1cbd4e6d99e F src/loadext.c 1c7a61ce1281041f437333f366a96aa0d29bb581 -F src/log.c d89988bb26a3cd414858c97642a612b4ce6e540f +F src/log.c 78575e5bed43578580a0ee86fd3c2e924abe4a88 F src/log.h a2654af46ce7b5732f4d5a731abfdd180f0a06d9 F src/main.c c0e7192bad5b90544508b241eb2487ac661de890 F src/malloc.c a08f16d134f0bfab6b20c3cd142ebf3e58235a6a @@ -160,7 +160,7 @@ F src/parse.y ace5c7a125d9f2a410e431ee3209034105045f7e F src/pcache.c ace8f6a5ecd4711cc66a1b23053be7109bd437cf F src/pcache.h c683390d50f856d4cd8e24342ae62027d1bb6050 F src/pcache1.c 6dc1871ce8ead9187161c370a58cd06c84221f76 -F src/pragma.c 9f1d53d40b47743f3d6151df15915970367c9de5 +F src/pragma.c f12cb58a8aa0d80cfed282ef87a285ed71beb793 F src/prepare.c fd1398cb1da54385ba5bd68d93928f10d10a1d9c F src/printf.c 5f5b65a83e63f2096a541a340722a509fa0240a7 F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50 @@ -756,10 +756,10 @@ F test/vtabE.test 7c4693638d7797ce2eda17af74292b97e705cc61 F test/vtab_alter.test 9e374885248f69e251bdaacf480b04a197f125e5 F test/vtab_err.test 0d4d8eb4def1d053ac7c5050df3024fd47a3fbd8 F test/vtab_shared.test 0eff9ce4f19facbe0a3e693f6c14b80711a4222d -F test/wal.test d5916b3a40d74dd5bf6ff21b42e6fa7f255efd13 +F test/wal.test a4be3c7a36e3db3d9d276dc7664c2ed5eece1e8a F test/walcrash.test 45cfbab30bb7cbe0b2e9d5cabe90dbcad10cb89b F test/walslow.test 38076d5fad49e3678027be0f8110e6a32d531dc2 -F test/walthread.test 1592116f74261d77ea697d9503b0cd1ab1c15e74 +F test/walthread.test 58cd64b06f186251f09f64e4918fb74a7e52c963 F test/where.test de337a3fe0a459ec7c93db16a519657a90552330 F test/where2.test 45eacc126aabb37959a387aa83e59ce1f1f03820 F test/where3.test 97d3936e6a443b968f1a61cdcc0f673252000e94 @@ -803,7 +803,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f -P f4e1150fed2c520c7c52612cb1019429d78dc32a -R da172d13acb33c194abd32bf4ed20cc9 +P 3f958e87c33d667d299b03ffdef58db5dc6363f4 +R 91ba1bf1aecd8bbf6dbeda941d0dbe58 U dan -Z cd10eeca0187a805032040fdd27191f0 +Z d3b26d31320c12902d5354af18b08451 diff --git a/manifest.uuid b/manifest.uuid index 43b6c91f02..8cd6641af0 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -3f958e87c33d667d299b03ffdef58db5dc6363f4 \ No newline at end of file +a9617eff39177250e2f118f25fdd4b3acb8b0478 \ No newline at end of file diff --git a/src/log.c b/src/log.c index 2b2764f361..c7fb8738d2 100644 --- a/src/log.c +++ b/src/log.c @@ -617,7 +617,7 @@ int sqlite3LogOpen( const char *zDb, /* Name of database file */ Log **ppLog /* OUT: Allocated Log handle */ ){ - int rc; /* Return Code */ + int rc = SQLITE_OK; /* Return Code */ Log *pRet; /* Object to allocate and return */ LogSummary *pSummary = 0; /* Summary object */ sqlite3_mutex *mutex = 0; /* LOG_SUMMARY_MUTEX mutex */ @@ -806,7 +806,7 @@ static int logCheckpoint( int pgsz = pLog->hdr.pgsz; /* Database page-size */ LogCheckpoint *pIter = 0; /* Log iterator context */ u32 iDbpage = 0; /* Next database page to write */ - u32 iFrame; /* Log frame containing data for iDbpage */ + u32 iFrame = 0; /* Log frame containing data for iDbpage */ /* Allocate the iterator */ pIter = logCheckpointInit(pLog); @@ -1080,6 +1080,77 @@ static int logLockRegion(Log *pLog, u32 mRegion, int op){ return SQLITE_OK; } +/* +** Try to read the log-summary header. Attempt to verify the header +** checksum. If the checksum can be verified, copy the log-summary +** header into structure pLog->hdr. If the contents of pLog->hdr are +** modified by this and pChanged is not NULL, set *pChanged to 1. +** Otherwise leave *pChanged unmodified. +** +** If the checksum cannot be verified return SQLITE_ERROR. +*/ +int logSummaryTryHdr(Log *pLog, int *pChanged){ + u32 aCksum[2] = {1, 1}; + u32 aHdr[LOGSUMMARY_HDR_NFIELD+2]; + + /* First try to read the header without a lock. Verify the checksum + ** before returning. This will almost always work. + */ + memcpy(aHdr, pLog->pSummary->aData, sizeof(aHdr)); + logChecksumBytes((u8*)aHdr, sizeof(u32)*LOGSUMMARY_HDR_NFIELD, aCksum); + if( aCksum[0]!=aHdr[LOGSUMMARY_HDR_NFIELD] + || aCksum[1]!=aHdr[LOGSUMMARY_HDR_NFIELD+1] + ){ + return SQLITE_ERROR; + } + + if( memcmp(&pLog->hdr, aHdr, sizeof(LogSummaryHdr)) ){ + if( pChanged ){ + *pChanged = 1; + } + memcpy(&pLog->hdr, aHdr, sizeof(LogSummaryHdr)); + } + return SQLITE_OK; +} + +/* +** Read the log-summary header from the log-summary file into structure +** pLog->hdr. If attempting to verify the header checksum fails, try +** to recover the log before returning. +** +** If the log-summary header is successfully read, return SQLITE_OK. +** Otherwise an SQLite error code. +*/ +int logSummaryReadHdr(Log *pLog, int *pChanged){ + int rc; + + /* First try to read the header without a lock. Verify the checksum + ** before returning. This will almost always work. + */ + if( SQLITE_OK==logSummaryTryHdr(pLog, pChanged) ){ + return SQLITE_OK; + } + + /* If the first attempt to read the header failed, lock the log-summary + ** file and try again. If the header checksum verification fails this + ** time as well, run log recovery. + */ + if( SQLITE_OK==(rc = logEnterMutex(pLog)) ){ + if( SQLITE_OK!=logSummaryTryHdr(pLog, pChanged) ){ + if( pChanged ){ + *pChanged = 1; + } + rc = logSummaryRecover(pLog->pSummary, pLog->pFd); + if( rc==SQLITE_OK ){ + rc = logSummaryTryHdr(pLog, 0); + } + } + logLeaveMutex(pLog); + } + + return rc; +} + /* ** Lock a snapshot. ** @@ -1122,31 +1193,7 @@ int sqlite3LogOpenSnapshot(Log *pLog, int *pChanged){ return rc; } - if( SQLITE_OK==(rc = logEnterMutex(pLog)) ){ - u32 aCksum[2] = {1, 1}; - u32 aHdr[LOGSUMMARY_HDR_NFIELD+2]; - memcpy(aHdr, pLog->pSummary->aData, sizeof(aHdr)); - - /* Verify the checksum on the log-summary header. If it fails, - ** recover the log-summary from the log file. - */ - logChecksumBytes((u8*)aHdr, sizeof(u32)*LOGSUMMARY_HDR_NFIELD, aCksum); - if( aCksum[0]!=aHdr[LOGSUMMARY_HDR_NFIELD] - || aCksum[1]!=aHdr[LOGSUMMARY_HDR_NFIELD+1] - ){ - rc = logSummaryRecover(pLog->pSummary, pLog->pFd); - memcpy(aHdr, pLog->pSummary->aData, sizeof(aHdr)); - *pChanged = 1; - } - if( rc==SQLITE_OK ){ - if( memcmp(&pLog->hdr, aHdr, sizeof(LogSummaryHdr)) ){ - *pChanged = 1; - memcpy(&pLog->hdr, aHdr, LOGSUMMARY_HDR_NFIELD*sizeof(u32)); - } - } - logLeaveMutex(pLog); - } - + rc = logSummaryReadHdr(pLog, pChanged); if( rc!=SQLITE_OK ){ /* An error occured while attempting log recovery. */ sqlite3LogCloseSnapshot(pLog); @@ -1409,8 +1456,13 @@ int sqlite3LogFrames( } /* -** Checkpoint the database. When this function is called the caller -** must hold an exclusive lock on the database file. +** Checkpoint the database: +** +** 1. Wait for an EXCLUSIVE lock on regions B and C. +** 2. Wait for an EXCLUSIVE lock on region A. +** 3. Copy the contents of the log into the database file. +** 4. Zero the log-summary header (so new readers will ignore the log). +** 5. Drop the locks obtained in steps 1 and 2. */ int sqlite3LogCheckpoint( Log *pLog, /* Log connection */ @@ -1419,20 +1471,30 @@ int sqlite3LogCheckpoint( int (*xBusyHandler)(void *), /* Pointer to busy-handler function */ void *pBusyHandlerArg /* Argument to pass to xBusyHandler */ ){ - int rc; + int rc; /* Return code */ + /* Wait for a write-lock on regions B and C. */ do { rc = logLockRegion(pLog, LOG_REGION_B|LOG_REGION_C, LOG_WRLOCK); }while( rc==SQLITE_BUSY && xBusyHandler(pBusyHandlerArg) ); if( rc!=SQLITE_OK ) return rc; + /* Wait for a write-lock on region A. */ do { rc = logLockRegion(pLog, LOG_REGION_A, LOG_WRLOCK); }while( rc==SQLITE_BUSY && xBusyHandler(pBusyHandlerArg) ); - if( rc!=SQLITE_OK ) return rc; - - rc = logCheckpoint(pLog, pFd, zBuf); + if( rc!=SQLITE_OK ){ + logLockRegion(pLog, LOG_REGION_B|LOG_REGION_C, LOG_UNLOCK); + return rc; + } + /* Copy data from the log to the database file. */ + rc = logSummaryReadHdr(pLog, 0); + if( rc==SQLITE_OK ){ + rc = logCheckpoint(pLog, pFd, zBuf); + } + + /* Release the locks. */ logLockRegion(pLog, LOG_REGION_A|LOG_REGION_B|LOG_REGION_C, LOG_UNLOCK); return rc; } diff --git a/src/pragma.c b/src/pragma.c index 95310e336c..137ff510d9 100644 --- a/src/pragma.c +++ b/src/pragma.c @@ -1386,7 +1386,6 @@ void sqlite3Pragma( if( sqlite3StrICmp(zLeft, "checkpoint")==0 ){ sqlite3VdbeUsesBtree(v, iDb); - sqlite3VdbeAddOp2(v, OP_Transaction, iDb, 1); sqlite3VdbeAddOp3(v, OP_Checkpoint, iDb, 0, 0); }else diff --git a/test/wal.test b/test/wal.test index b12782b663..cafda909a4 100644 --- a/test/wal.test +++ b/test/wal.test @@ -27,7 +27,7 @@ proc range {args} { } proc reopen_db {} { - db close + catch { db close } file delete -force test.db test.db-wal sqlite3_wal db test.db #register_logtest @@ -223,7 +223,6 @@ do_test wal-7.1 { CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 2); } - list [file size test.db] [file size test.db-wal] } [list 0 [expr (1024+20)*3]] do_test wal-7.2 { @@ -231,272 +230,17 @@ do_test wal-7.2 { list [file size test.db] [file size test.db-wal] } [list 2048 [expr (1024+20)*3]] -# db close -# sqlite3_wal db test.db -# register_logsummary_module db -# # Warm-body tests of the virtual tables used for testing. -# # -# do_test wal-8.1 { -# execsql { CREATE VIRTUAL TABLE temp.logsummary USING logsummary } -# execsql { CREATE VIRTUAL TABLE temp.logcontent USING logcontent } -# execsql { CREATE VIRTUAL TABLE temp.loglock USING loglock } -# execsql { SELECT * FROM logsummary } -# } [list [file join [pwd] test.db] 1 0 0 0 0 0 0] -# -# do_test wal-8.2 { -# sqlite3_wal db2 test.db -# execsql { SELECT * FROM logsummary } -# } [list [file join [pwd] test.db] 2 0 0 0 0 0 0] -# do_test wal-8.3 { -# db2 close -# execsql { SELECT * FROM logsummary } -# } [list [file join [pwd] test.db] 1 0 0 0 0 0 0] -# do_test wal-8.4 { -# execsql { INSERT INTO t1 VALUES(3, 4) } -# execsql { SELECT * FROM logsummary } -# } [list [file join [pwd] test.db] 1 1024 2 2 3 0 0] -# do_test wal-8.5 { -# execsql { PRAGMA checkpoint } -# execsql { SELECT * FROM logsummary } -# } [list [file join [pwd] test.db] 1 1024 0 0 0 0 0] -# -# do_test wal-8.6 { -# execsql { INSERT INTO t1 VALUES(5, 6) } -# execsql { PRAGMA checkpoint('1 1 1') } -# execsql { SELECT * FROM logsummary } -# } [list [file join [pwd] test.db] 1 1024 2 0 3 0 0] -# do_test wal-8.7 { -# execsql { SELECT logpage, dbpage FROM logcontent } -# } {} -# do_test wal-8.8 { -# execsql { INSERT INTO t1 VALUES(7, 8) } -# execsql { SELECT logpage, dbpage FROM logcontent } -# } {4 T:4 5 2} -# do_test wal-8.9 { -# execsql { SELECT * FROM logsummary } -# } [list [file join [pwd] test.db] 1 1024 2 4 5 0 0] -# do_test wal-8.10 { -# execsql { SELECT * FROM loglock } -# } [list [file join [pwd] test.db] 0 0 0] -# do_test wal-8.11 { -# execsql { BEGIN; SELECT * FROM t1; } -# execsql { SELECT * FROM loglock } -# } [list [file join [pwd] test.db] 0 0 4] -# -# # Try making the log wrap around. -# # -# reopen_db -# -# do_test wal-9.1 { -# execsql { -# BEGIN; -# CREATE TABLE t1(a PRIMARY KEY, b); -# } -# for {set i 0} {$i < 100} {incr i} { -# execsql { INSERT INTO t1 VALUES($i, randomblob(100)) } -# } -# execsql COMMIT -# execsql { SELECT * FROM logsummary } -# } [list [file join [pwd] test.db] 1 1024 2 2 17 0 0] -# do_test wal-9.2 { -# execsql { SELECT logpage, dbpage FROM logcontent } -# } {2 T:2 3 1 4 2 5 3 6 4 7 5 8 6 9 7 10 8 11 9 12 10 13 11 14 12 15 13 16 14 17 15} -# do_test wal-9.3 { -# execsql { PRAGMA checkpoint('12, 12') } -# execsql { SELECT * FROM logsummary } -# } [list [file join [pwd] test.db] 1 1024 2 15 17 0 0] -# do_test wal-9.4 { -# execsql { SELECT logpage, dbpage FROM logcontent } -# } {15 13 16 14 17 15} -# do_test wal-9.5 { -# execsql { SELECT count(*) FROM t1 } -# } {100} -# do_test wal-9.6 { -# execsql { INSERT INTO t1 VALUES(100, randomblob(100)) } -# execsql { SELECT * FROM logsummary } -# } [list [file join [pwd] test.db] 1 1024 2 15 20 0 0] -# -# do_test wal-9.7 { -# execsql { SELECT count(*) FROM t1 } -# } {101} -# do_test wal-9.8 { -# db close -# sqlite3_wal db test.db -# register_logtest -# execsql { SELECT count(*) FROM t1 } -# } {101} -# do_test wal-9.9 { -# execsql { SELECT * FROM logsummary } -# } [list [file join [pwd] test.db] 1 0 0 0 0 0 0] -# -# reopen_db -# do_test wal-10.1 { -# execsql { -# PRAGMA page_size = 1024; -# CREATE TABLE t1(x PRIMARY KEY); -# INSERT INTO t1 VALUES(randomblob(900)); -# INSERT INTO t1 VALUES(randomblob(900)); -# INSERT INTO t1 SELECT randomblob(900) FROM t1; -- 4 -# INSERT INTO t1 SELECT randomblob(900) FROM t1; -- 8 -# INSERT INTO t1 SELECT randomblob(900) FROM t1; -- 16 -# } -# list [file size test.db] [file size test.db-wal] -# } {0 55296} -# do_test wal-10.2 { -# execsql { PRAGMA checkpoint('20 30') } -# } {} -# do_test wal-10.3 { -# execsql { SELECT * FROM logsummary } -# } [list [file join [pwd] test.db] 1 1024 34 38 54 0 0] -# do_test wal-10.4 { -# execsql { SELECT dbpage FROM logcontent } -# } {21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37} -# do_test wal-10.5 { -# execsql { INSERT INTO t1 VALUES(randomblob(900)) } -# execsql { SELECT * FROM logsummary } -# } [list [file join [pwd] test.db] 1 1024 34 38 6 34 54] -# do_test wal-10.6 { -# execsql { SELECT count(*) FROM t1 WHERE x NOT NULL } -# } {17} -# do_test wal-10.8 { -# execsql { SELECT logpage FROM logcontent } -# } [range 38 54 1 6] -# do_test wal-10.9 { -# execsql { INSERT INTO t1 SELECT randomblob(900) FROM t1 } -# execsql { SELECT * FROM logsummary } -# } [list [file join [pwd] test.db] 1 1024 34 38 68 34 54] -# -# do_test wal-10.10 { -# execsql { SELECT logpage FROM logcontent } -# } [range 38 54 1 33 55 68] -# -# do_test wal-10.11 { -# execsql { SELECT count(*) FROM t1 WHERE x NOT NULL } -# } {34} -# -# do_test wal-10.12 { -# execsql { PRAGMA checkpoint('35 35') } -# } {} -# do_test wal-10.13 { -# execsql { SELECT logpage FROM logcontent } -# } [range 22 68] -# do_test wal-10.13a { -# execsql { SELECT dbpage FROM logcontent } -# } [list \ -# 50 51 52 53 54 55 56 57 58 59 60 61 \ -# 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 \ -# 62 63 64 65 66 67 68 69 70 71 72 73 74 75 \ -# ] -# -# do_test wal-10.14 { -# execsql { SELECT count(*) FROM t1 WHERE x NOT NULL } -# } {34} -# do_test wal-10.15 { -# execsql { PRAGMA integrity_check } -# } {ok} -# do_test wal-10.16 { -# execsql { PRAGMA checkpoint('20 20') } -# execsql { SELECT * FROM logsummary } -# } [list [file join [pwd] test.db] 1 1024 7 63 68 0 0] -# do_test wal-10.17 { -# execsql { SELECT logpage FROM logcontent } -# } [range 63 68] -# do_test wal-10.17a { -# execsql { SELECT dbpage FROM logcontent } -# } {70 71 72 73 74 75} -# -# do_test wal-10.18 { -# execsql { INSERT INTO t1 SELECT randomblob(900) FROM t1 } -# execsql { SELECT logpage FROM logcontent } -# } [range 63 147] -# integrity_check wal-10.19 -# -# do_test wal-10.20 { -# execsql { PRAGMA checkpoint('52 52') } -# execsql { SELECT logpage FROM logcontent } -# } [range 116 147] -# do_test wal-10.20a { -# execsql { SELECT * FROM logsummary } -# } [list [file join [pwd] test.db] 1 1024 69 116 147 0 0] -# integrity_check wal-10.20.integrity -# -# do_test wal-10.21 { -# execsql { INSERT INTO t1 VALUES( randomblob(900) ) } -# execsql { SELECT logpage FROM logcontent } -# } [range 116 152] -# do_test wal-10.22 { -# execsql { PRAGMA integrity_check } -# } {ok} -# -# file delete -force testX.db testX.db-wal -# file copy test.db testX.db -# file copy test.db-wal testX.db-wal -# do_test wal-10.23 { -# sqlite3_wal db2 testX.db -# register_logtest db2 -# execsql { SELECT logpage FROM logcontent WHERE db LIKE '%testX%' } db2 -# } [range 34 54 1 33 55 152] -# -# do_test wal-10.24 { -# execsql { PRAGMA integrity_check } db2 -# } {ok} -# db2 close -# -# do_test wal-11.1 { -# reopen_db -# sqlite3_wal db2 test.db -# -# execsql { -# BEGIN; -# CREATE TABLE t1(x); -# CREATE TABLE t2(x PRIMARY KEY); -# INSERT INTO t1 VALUES(randomblob(900)); -# INSERT INTO t1 VALUES(randomblob(900)); -# INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 4 */ -# INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 8 */ -# INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 16 */ -# INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 32 */ -# INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 64 */ -# -# INSERT INTO t2 VALUES('x'); -# INSERT INTO t2 VALUES('y'); -# INSERT INTO t2 VALUES('z'); -# COMMIT; -# SELECT * FROM logsummary; -# } -# } [list [file join [pwd] test.db] 2 1024 2 2 70 0 0] -# -# do_test wal-11.2 { -# execsql { -# BEGIN; SELECT x FROM t2; -# } db2 -# } {x y z} -# do_test wal-11.2 { -# execsql { -# INSERT INTO t1 VALUES(randomblob(900)); -# PRAGMA checkpoint('10 100'); -# INSERT INTO t1 VALUES(randomblob(900)); -# INSERT INTO t2 VALUES('0'); -# SELECT * FROM logsummary; -# } -# } [list [file join [pwd] test.db] 2 1024 71 71 7 71 73] -# do_test wal-12.3 { -# execsql { PRAGMA integrity_check } db2 -# } {ok} -# db2 close - - # Execute some transactions in auto-vacuum mode to test database file # truncation. # -do_test wal-12.1 { +do_test wal-8.1 { reopen_db execsql { PRAGMA auto_vacuum = 1; PRAGMA auto_vacuum; } } {1} -do_test wal-12.2 { +do_test wal-8.2 { execsql { PRAGMA page_size = 1024; CREATE TABLE t1(x); @@ -511,7 +255,7 @@ do_test wal-12.2 { } file size test.db } [expr 68*1024] -do_test wal-12.3 { +do_test wal-8.3 { execsql { DELETE FROM t1 WHERE rowid<54; PRAGMA checkpoint; @@ -522,7 +266,7 @@ do_test wal-12.3 { # Run some "warm-body" tests to ensure that log-summary files with more # than 256 entries (log summaries that contain index blocks) work Ok. # -do_test wal-13.1 { +do_test wal-9.1 { reopen_db execsql { PRAGMA page_size = 1024; @@ -539,12 +283,12 @@ do_test wal-13.1 { } file size test.db } 0 -do_test wal-13.2 { +do_test wal-9.2 { sqlite3_wal db2 test.db execsql {PRAGMA integrity_check } db2 } {ok} -do_test wal-13.3 { +do_test wal-9.3 { file delete -force test2.db test2.db-wal file copy test.db test2.db file copy test.db-wal test2.db-wal @@ -553,8 +297,7 @@ do_test wal-13.3 { } {ok} db3 close -do_test wal-13.4 { -breakpoint +do_test wal-9.4 { execsql { PRAGMA checkpoint } db2 close sqlite3_wal db2 test.db @@ -564,5 +307,102 @@ breakpoint foreach handle {db db2 db3} { catch { $handle close } } unset handle +#------------------------------------------------------------------------- +# The following block of tests - wal-10.* - test that the WAL locking +# scheme works for clients in a single process. +# +reopen_db +sqlite3_wal db2 test.db +sqlite3_wal db3 test.db + +do_test wal-10.1 { + execsql { + CREATE TABLE t1(a, b); + INSERT INTO t1 VALUES(1, 2); + BEGIN; + INSERT INTO t1 VALUES(3, 4); + } + execsql "SELECT * FROM t1" db2 +} {1 2} +do_test wal-10.2 { + execsql { COMMIT } + execsql "SELECT * FROM t1" db2 +} {1 2 3 4} +do_test wal-10.3 { + execsql { + BEGIN; + SELECT * FROM t1; + } db2 +} {1 2 3 4} +do_test wal-10.4 { + catchsql { PRAGMA checkpoint } +} {1 {database is locked}} +do_test wal-10.5 { + execsql { INSERT INTO t1 VALUES(5, 6) } + execsql { SELECT * FROM t1 } db2 +} {1 2 3 4} + +# Connection [db2] is holding a lock on a snapshot, preventing [db] from +# checkpointing the database. Add a busy-handler to [db]. If [db2] completes +# its transaction from within the busy-handler, [db] is able to complete +# the checkpoint operation. +# +proc busyhandler x { + if {$x==4} { + execsql { COMMIT } db2 + } + if {$x<5} {return 0} + return 1 +} +db busy busyhandler +do_test wal-10.6 { + execsql { PRAGMA checkpoint } +} {} + +# Similar to the test above. Except this time, a new read transaction is +# started (db3) while the checkpointer is waiting for an old one to finish. +# The checkpointer can finish, but any subsequent write operations must +# wait until after db3 has closed the read transaction. +# +db busy {} +do_test wal-10.7 { + execsql { + BEGIN; + SELECT * FROM t1; + } db2 +} {1 2 3 4 5 6} +do_test wal-10.8 { + execsql { INSERT INTO t1 VALUES(7, 8) } + catchsql { PRAGMA checkpoint } +} {1 {database is locked}} +proc busyhandler x { + if {$x==3} { execsql { BEGIN; SELECT * FROM t1 } db3 } + if {$x==4} { execsql { COMMIT } db2 } + if {$x<5} { return 0 } + return 1 +} +db busy busyhandler +do_test wal-10.9 { + execsql { PRAGMA checkpoint } +} {} +do_test wal-10.10 { + execsql { SELECT * FROM t1 } db3 +} {1 2 3 4 5 6 7 8} +do_test wal-10.11 { + catchsql { INSERT INTO t1 VALUES(9, 10) } +} {1 {database is locked}} +do_test wal-10.12 { + execsql { SELECT * FROM t1 } +} {1 2 3 4 5 6 7 8} +do_test wal-10.13 { + execsql { COMMIT } db3 +} {} +do_test wal-10.14 { + execsql { INSERT INTO t1 VALUES(9, 10) } + execsql { SELECT * FROM t1 } +} {1 2 3 4 5 6 7 8 9 10} + +foreach handle {db db2 db3} { catch { $handle close } } +unset handle finish_test diff --git a/test/walthread.test b/test/walthread.test index 7727ab3759..fbd6c653cc 100644 --- a/test/walthread.test +++ b/test/walthread.test @@ -62,7 +62,7 @@ do_test walthread-1.4 { # Each of the N threads runs N read transactions followed by a single write # transaction in a loop as fast as possible. # -# There is also a single checkpointer thread. It runs the following loop: +# Ther is also a single checkpointer thread. It runs the following loop: # # 1) Execute "CHECKPOINT main 32 -1 1" # 2) Sleep for 500 ms. @@ -85,7 +85,7 @@ set thread_program { set rc [sqlite3_finalize $stmt] if {$rc ne "SQLITE_OK"} { - error $rc + error "$rc: [sqlite3_errmsg $DB]" } return $res }