]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add tests and fix bugs in WAL locking mechanism.
authordan <dan@noemail.net>
Wed, 14 Apr 2010 18:50:08 +0000 (18:50 +0000)
committerdan <dan@noemail.net>
Wed, 14 Apr 2010 18:50:08 +0000 (18:50 +0000)
FossilOrigin-Name: c18077f2465fc34830f11c9832e76be5746eaeea

manifest
manifest.uuid
src/log.c
test/wal.test

index 92200a9b4f302ac1ba95ebd51e74fec62282adc1..af8652faeb117be24d74166053f25404c2d6e751 100644 (file)
--- 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
index e7339388faa2a9c54e3954ebce52ff6cc28aab20..6a496e5b8e4f72dcdef7e565edf742e3b6daf4de 100644 (file)
@@ -1 +1 @@
-9435f3135849e0d38fde1669201db508561a6308
\ No newline at end of file
+c18077f2465fc34830f11c9832e76be5746eaeea
\ No newline at end of file
index f1481bf92adcb6ade32bb14c71f2fa02ebea118a..30d9d8ded6600d21701e4126cc87c61ca4839a3d 100644 (file)
--- 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;
index 99a19fe40048388528f56c980682f5459904e5d3..9ac00be677b9d04ddba58a37f5734866bb37adad 100644 (file)
@@ -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 } }