From: dan Date: Wed, 14 Apr 2010 18:50:08 +0000 (+0000) Subject: Add tests and fix bugs in WAL locking mechanism. X-Git-Tag: version-3.7.2~455^2~81 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=49320f8568e71fb2a8796d14233256e69b67095b;p=thirdparty%2Fsqlite.git Add tests and fix bugs in WAL locking mechanism. FossilOrigin-Name: c18077f2465fc34830f11c9832e76be5746eaeea --- diff --git a/manifest b/manifest index 92200a9b4f..af8652faeb 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\stests\sto\scheck\sinter-process\sWAL\slocking. -D 2010-04-14T18:06:51 +C Add\stests\sand\sfix\sbugs\sin\sWAL\slocking\smechanism. +D 2010-04-14T18:50:08 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 a3558ae5a48f808134db9b77a893141ba79402e7 +F src/log.c 165addfd51fa9581936ec04914b17a3b274c49bd F src/log.h a2654af46ce7b5732f4d5a731abfdd180f0a06d9 F src/main.c c0e7192bad5b90544508b241eb2487ac661de890 F src/malloc.c a08f16d134f0bfab6b20c3cd142ebf3e58235a6a @@ -757,7 +757,7 @@ 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 812dde0a689f69ce9b2d897ce4f08d752bd06749 +F test/wal.test 8f480128b140e54253684bc395f7af0254dc4e03 F test/walcrash.test 45cfbab30bb7cbe0b2e9d5cabe90dbcad10cb89b F test/walslow.test 38076d5fad49e3678027be0f8110e6a32d531dc2 F test/walthread.test 58cd64b06f186251f09f64e4918fb74a7e52c963 @@ -804,7 +804,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f -P 5e9dd3bd8e829376408925fb4cfcd5bb1eb1105f -R b62050501d5438f40040e5ea09ab63d7 +P 9435f3135849e0d38fde1669201db508561a6308 +R 271de1f1c02e1bce44bf2011bef4ef6b U dan -Z 3f45f30ce9e36f0f15e4d7966accef62 +Z 6918d2483c611afa56b459efd78f482c diff --git a/manifest.uuid b/manifest.uuid index e7339388fa..6a496e5b8e 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -9435f3135849e0d38fde1669201db508561a6308 \ No newline at end of file +c18077f2465fc34830f11c9832e76be5746eaeea \ No newline at end of file diff --git a/src/log.c b/src/log.c index f1481bf92a..30d9d8ded6 100644 --- a/src/log.c +++ b/src/log.c @@ -1004,6 +1004,7 @@ static int logLockRegion(Log *pLog, u32 mRegion, int op){ /* Region D reader lock operations */ || (op==LOG_RDLOCK && mRegion==(LOG_REGION_D)) + || (op==LOG_RDLOCK && mRegion==(LOG_REGION_A)) || (op==LOG_UNLOCK && mRegion==(LOG_REGION_D)) /* Checkpointer lock operations */ @@ -1334,12 +1335,9 @@ void sqlite3LogMaxpgno(Log *pLog, Pgno *pPgno){ } /* -** The caller must hold at least a RESERVED lock on the database file -** when invoking this function. -** ** This function returns SQLITE_OK if the caller may write to the database. ** Otherwise, if the caller is operating on a snapshot that has already -** been overwritten by another writer, SQLITE_OBE is returned. +** been overwritten by another writer, SQLITE_BUSY is returned. */ int sqlite3LogWriteLock(Log *pLog, int op){ assert( pLog->isLocked ); @@ -1351,14 +1349,26 @@ int sqlite3LogWriteLock(Log *pLog, int op){ return rc; } - /* TODO: What if this is a region D reader? And after writing this - ** transaction it continues to hold a read-lock on the db? Maybe we - ** need to switch it to a region A reader here so that unlocking C|D - ** does not leave the connection with no lock at all. + /* If this is connection is a region D, then the SHARED lock on region + ** D has just been upgraded to EXCLUSIVE. But no lock at all is held on + ** region A. This means that if the write-transaction is committed + ** and this connection downgrades to a reader, it will be left with no + ** lock at all. And its snapshot could get clobbered by a checkpoint + ** operation. + ** + ** To stop this from happening, grab a SHARED lock on region A now. + ** This should always be successful, as the only time a client holds + ** an EXCLUSIVE lock on region A, it must also be holding an EXCLUSIVE + ** lock on region C (a checkpointer does this). This is not possible, + ** as this connection currently has the EXCLUSIVE lock on region C. */ - assert( pLog->isLocked!=LOG_REGION_D ); + if( pLog->isLocked==LOG_REGION_D ){ + logLockRegion(pLog, LOG_REGION_A, LOG_RDLOCK); + pLog->isLocked = LOG_REGION_A; + } if( memcmp(&pLog->hdr, pLog->pSummary->aData, sizeof(pLog->hdr)) ){ + logLockRegion(pLog, LOG_REGION_C|LOG_REGION_D, LOG_UNLOCK); return SQLITE_BUSY; } pLog->isWriteLocked = 1; diff --git a/test/wal.test b/test/wal.test index 99a19fe400..9ac00be677 100644 --- a/test/wal.test +++ b/test/wal.test @@ -447,26 +447,83 @@ foreach code [list { return 1 } db busy busyhandler - do_test wal-10.$tn.9 { + do_test wal-10.$tn.17 { execsql { PRAGMA checkpoint } } {} - do_test wal-10.$tn.10 { + do_test wal-10.$tn.18 { sql3 { SELECT * FROM t1 } } {1 2 3 4 5 6 7 8 9 10 11 12} - do_test wal-10.$tn.11 { + do_test wal-10.$tn.19 { catchsql { INSERT INTO t1 VALUES(13, 14) } } {1 {database is locked}} - do_test wal-10.$tn.12 { + do_test wal-10.$tn.20 { execsql { SELECT * FROM t1 } } {1 2 3 4 5 6 7 8 9 10 11 12} - do_test wal-10.$tn.13 { + do_test wal-10.$tn.21 { sql3 COMMIT } {} - do_test wal-10.$tn.14 { + do_test wal-10.$tn.22 { execsql { INSERT INTO t1 VALUES(13, 14) } execsql { SELECT * FROM t1 } } {1 2 3 4 5 6 7 8 9 10 11 12 13 14} + # Set [db3] up as a "region D" reader again. Then upgrade it to a writer + # and back down to a reader. Then, check that a checkpoint is not possible + # (as [db3] still has a snapshot locked). + # + do_test wal-10.$tn.23 { + execsql { PRAGMA checkpoint } + } {} + do_test wal-10.$tn.24 { + sql2 { BEGIN; SELECT * FROM t1; } + } {1 2 3 4 5 6 7 8 9 10 11 12 13 14} + do_test wal-10.$tn.25 { + execsql { PRAGMA checkpoint } + } {} + do_test wal-10.$tn.26 { + catchsql { INSERT INTO t1 VALUES(15, 16) } + } {1 {database is locked}} + do_test wal-10.$tn.27 { + sql3 { INSERT INTO t1 VALUES(15, 16) } + } {} + do_test wal-10.$tn.28 { + code3 { + set ::STMT [sqlite3_prepare db3 "SELECT * FROM t1" -1 TAIL] + sqlite3_step $::STMT + } + sql3 COMMIT + execsql { SELECT * FROM t1 } + } {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16} + db busy {} + do_test wal-10.$tn.29 { + execsql { INSERT INTO t1 VALUES(17, 18) } + catchsql { PRAGMA checkpoint } + } {1 {database is locked}} + do_test wal-10.$tn.30 { + code3 { sqlite3_finalize $::STMT } + execsql { PRAGMA checkpoint } + } {} + + # At one point, if a reader failed to upgrade to a writer because it + # was reading an old snapshot, the write-locks were not being released. + # Test that this bug has been fixed. + # + do_test wal-10.$tn.31 { + execsql { BEGIN ; SELECT * FROM t1 } + sql2 { INSERT INTO t1 VALUES(19, 20) } + catchsql { INSERT INTO t1 VALUES(21, 22) } + } {1 {database is locked}} + do_test wal-10.$tn.32 { + # This statement would fail when the bug was present. + sql2 { INSERT INTO t1 VALUES(21, 22) } + } {} + do_test wal-10.$tn.33 { + execsql { SELECT * FROM t1 ; COMMIT } + } {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18} + do_test wal-10.$tn.34 { + execsql { SELECT * FROM t1 } + } {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22} + catch { db close } catch { code2 { db2 close } } catch { code3 { db3 close } }