]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Changes so that WAL and exclusive-locking mode work together.
authordan <dan@noemail.net>
Tue, 11 May 2010 12:19:26 +0000 (12:19 +0000)
committerdan <dan@noemail.net>
Tue, 11 May 2010 12:19:26 +0000 (12:19 +0000)
FossilOrigin-Name: 71e7b1cf9f4cd02a2a9bc8a3e58acd7a7e3c7e60

manifest
manifest.uuid
src/pager.c
src/wal.c
src/wal.h
test/wal2.test

index 332497bed4c6f2adc5aa5c83685209e57a06ea97..07ee6bc4b4b712d9307a6133d77a4d3743dec060 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Updates\sfor\sto\sWAL\sTCL\stest\sscripts\sto\ssupport\srunning\son\sWindows.
-D 2010-05-11T02:46:17
+C Changes\sso\sthat\sWAL\sand\sexclusive-locking\smode\swork\stogether.
+D 2010-05-11T12:19:27
 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0
 F Makefile.in a5cad1f8f3e021356bfcc6c77dc16f6f1952bbc3
 F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
@@ -154,7 +154,7 @@ F src/os_common.h 0d6ee583b6ee3185eb9d951f890c6dd03021a08d
 F src/os_os2.c 8ad77a418630d7dee91d1bb04f79c2096301d3a0
 F src/os_unix.c 34fe71c67fce72360411d60fe069c7f0dc612dd0
 F src/os_win.c a8fc01d8483be472e495793c01064fd87e56a5c1
-F src/pager.c a47af9c2c9ca425bd68642d61764266331a3323f
+F src/pager.c 871ccb53e901dabf92b9b2806f0fbe4a2c039d99
 F src/pager.h 934b598583a9d936bb13c37d62a2fe68ac48781c
 F src/parse.y ace5c7a125d9f2a410e431ee3209034105045f7e
 F src/pcache.c ace8f6a5ecd4711cc66a1b23053be7109bd437cf
@@ -224,8 +224,8 @@ F src/vdbeblob.c 5327132a42a91e8b7acfb60b9d2c3b1c5c863e0e
 F src/vdbemem.c 2a82f455f6ca6f78b59fb312f96054c04ae0ead1
 F src/vdbetrace.c 864cef96919323482ebd9986f2132435115e9cc2
 F src/vtab.c a0f8a40274e4261696ef57aa806de2776ab72cda
-F src/wal.c 65d1376c8d5ce500ac1fd1bf49e2287ad04cf6c4
-F src/wal.h b4c42014b5fa3b4e6244ac8c65de7ff67adeb27c
+F src/wal.c 7042647fd4c89b789da6dc934550effdf573a290
+F src/wal.h 32f36b2a827b78373658dac5521291485dfa52b6
 F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f
 F src/where.c 75fee9e255b62f773fcadd1d1f25b6f63ac7a356
 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
@@ -762,7 +762,7 @@ F test/vtab_alter.test 9e374885248f69e251bdaacf480b04a197f125e5
 F test/vtab_err.test 0d4d8eb4def1d053ac7c5050df3024fd47a3fbd8
 F test/vtab_shared.test 0eff9ce4f19facbe0a3e693f6c14b80711a4222d
 F test/wal.test 7a100918c45872fa19cfb4413299b9afb08727b6
-F test/wal2.test 0f53c711d6530d3c7aba46752aef9fd44b708c6c
+F test/wal2.test 913fc65e533593e3b5dfb193340ac32368da1914
 F test/walbak.test a0e45187c7d8928df035dfea29b99b016b21ca3c
 F test/walcrash.test f6d5fb2bb108876f04848720a488065d9deef69f
 F test/walfault.test 98df47444944a6db2161eed5cef71d6c00bcb8c3
@@ -813,7 +813,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
 F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
 F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
-P 6e3735f72cb7d2f4d16c8f9bc59ff159c75243e5
-R 6271f8fbb605ce59c9bcaa6c5c92af66
-U shaneh
-Z 6c6a591f5b1ea69fe4a6d39da0e8aca9
+P 6a5630806c87b0f4e5632c37c357f98effd9608a
+R ed8f197401fc52de24ef292a838d5248
+U dan
+Z ca9adf09cbf258f90f9e808c81428995
index a244475a7b8fe347db22bc24dd67d91f6a2a907d..9d9a0656d7e830e19390e4772e36bb02cfba9314 100644 (file)
@@ -1 +1 @@
-6a5630806c87b0f4e5632c37c357f98effd9608a
\ No newline at end of file
+71e7b1cf9f4cd02a2a9bc8a3e58acd7a7e3c7e60
\ No newline at end of file
index 221668c9fbea4c9a0d64a22480e41d2337137a95..47dee68f8bf57a4858e1953622dcb79d6341472b 100644 (file)
@@ -1404,8 +1404,8 @@ static int pager_end_transaction(Pager *pPager, int hasMaster){
       }
       pPager->journalOff = 0;
       pPager->journalStarted = 0;
-    }else if( pPager->exclusiveMode 
-     || pPager->journalMode==PAGER_JOURNALMODE_PERSIST
+    }else if( pPager->journalMode==PAGER_JOURNALMODE_PERSIST
+      || (pPager->exclusiveMode && pPager->journalMode!=PAGER_JOURNALMODE_WAL)
     ){
       rc = zeroJournalHdr(pPager, hasMaster);
       pager_error(pPager, rc);
@@ -1439,6 +1439,17 @@ static int pager_end_transaction(Pager *pPager, int hasMaster){
   if( pagerUseWal(pPager) ){
     rc2 = sqlite3WalWriteLock(pPager->pWal, 0);
     pPager->state = PAGER_SHARED;
+
+    /* If the connection was in locking_mode=exclusive mode but is no longer,
+    ** drop the EXCLUSIVE lock held on the database file.
+    */
+    if( rc2==SQLITE_OK 
+     && !pPager->exclusiveMode 
+     && sqlite3WalExclusiveMode(pPager->pWal, -1) 
+    ){
+      sqlite3WalExclusiveMode(pPager->pWal, 0);
+      rc2 = osUnlock(pPager->fd, SHARED_LOCK);
+    }
   }else if( !pPager->exclusiveMode ){
     rc2 = osUnlock(pPager->fd, SHARED_LOCK);
     pPager->state = PAGER_SHARED;
@@ -4509,15 +4520,34 @@ int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory){
   int rc = SQLITE_OK;
   assert( pPager->state!=PAGER_UNLOCK );
   pPager->subjInMemory = (u8)subjInMemory;
+
   if( pPager->state==PAGER_SHARED ){
     assert( pPager->pInJournal==0 );
     assert( !MEMDB && !pPager->tempFile );
 
     if( pagerUseWal(pPager) ){
+      /* If the pager is configured to use locking_mode=exclusive, and an
+      ** exclusive lock on the database is not already held, obtain it now.
+      */
+      if( pPager->exclusiveMode && !sqlite3WalExclusiveMode(pPager->pWal, -1) ){
+        rc = sqlite3OsLock(pPager->fd, EXCLUSIVE_LOCK);
+        pPager->state = PAGER_SHARED;
+        if( rc!=SQLITE_OK ){
+          return rc;
+        }
+        sqlite3WalExclusiveMode(pPager->pWal, 1);
+      }
+
       /* Grab the write lock on the log file. If successful, upgrade to
-      ** PAGER_EXCLUSIVE state. Otherwise, return an error code to the caller.
+      ** PAGER_RESERVED state. Otherwise, return an error code to the caller.
       ** The busy-handler is not invoked if another connection already
       ** holds the write-lock. If possible, the upper layer will call it.
+      **
+      ** WAL mode sets Pager.state to PAGER_RESERVED when it has an open
+      ** transaction, but never to PAGER_EXCLUSIVE. This is because in 
+      ** PAGER_EXCLUSIVE state the code to roll back savepoint transactions
+      ** may copy data from the sub-journal into the database file as well
+      ** as into the page cache. Which would be incorrect in WAL mode.
       */
       rc = sqlite3WalWriteLock(pPager->pWal, 1);
       if( rc==SQLITE_OK ){
@@ -4525,6 +4555,9 @@ int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory){
         pPager->state = PAGER_RESERVED;
         pPager->journalOff = 0;
       }
+
+      assert( rc!=SQLITE_OK || pPager->state==PAGER_RESERVED );
+      assert( rc==SQLITE_OK || pPager->state==PAGER_SHARED );
     }else{
       /* Obtain a RESERVED lock on the database file. If the exFlag parameter
       ** is true, then immediately upgrade this to an EXCLUSIVE lock. The
index 94b332041c33ecf7eeab8a837a805e17f6316a08..8e3c0c6469737039681522c93918ad84b61698fe 100644 (file)
--- a/src/wal.c
+++ b/src/wal.c
@@ -132,6 +132,7 @@ struct Wal {
   u32 *pWiData;              /* Pointer to wal-index content in memory */
   u8 lockState;              /* SQLITE_SHM_xxxx constant showing lock state */
   u8 readerType;             /* SQLITE_SHM_READ or SQLITE_SHM_READ_FULL */
+  u8 exclusiveMode;          /* Non-zero if connection is in exclusive mode */
   WalIndexHdr hdr;           /* Wal-index for current snapshot */
   char *zName;               /* Name of underlying storage */
 };
@@ -217,14 +218,17 @@ static void walChecksumBytes(u8 *aByte, int nByte, u32 *aCksum){
 ** in pWal->readerType.
 */
 static int walSetLock(Wal *pWal, int desiredStatus){
-  int rc, got;
-  if( pWal->lockState==desiredStatus ) return SQLITE_OK;
-  got = pWal->lockState;
-  rc = pWal->pVfs->xShmLock(pWal->pVfs, pWal->pWIndex, desiredStatus, &got);
-  pWal->lockState = got;
-  if( got==SQLITE_SHM_READ_FULL || got==SQLITE_SHM_READ ){
-    pWal->readerType = got;
-    pWal->lockState = SQLITE_SHM_READ;
+  int rc = SQLITE_OK;             /* Return code */
+  if( pWal->exclusiveMode || pWal->lockState==desiredStatus ){
+    pWal->lockState = desiredStatus;
+  }else{
+    int got = pWal->lockState;
+    rc = pWal->pVfs->xShmLock(pWal->pVfs, pWal->pWIndex, desiredStatus, &got);
+    pWal->lockState = got;
+    if( got==SQLITE_SHM_READ_FULL || got==SQLITE_SHM_READ ){
+      pWal->readerType = got;
+      pWal->lockState = SQLITE_SHM_READ;
+    }
   }
   return rc;
 }
@@ -1231,7 +1235,7 @@ int sqlite3WalWriteLock(Wal *pWal, int op){
         walSetLock(pWal, SQLITE_SHM_READ);
       }
     }
-  }else if( ALWAYS( pWal->lockState==SQLITE_SHM_WRITE ) ){
+  }else if( pWal->lockState==SQLITE_SHM_WRITE ){
     rc = walSetLock(pWal, SQLITE_SHM_READ);
   }
   return rc;
@@ -1250,18 +1254,20 @@ int sqlite3WalWriteLock(Wal *pWal, int op){
 ** function returns SQLITE_OK.
 */
 int sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *pUndoCtx){
-  int unused;
-  int rc;
-  Pgno iMax = pWal->hdr.iLastPg;
-  Pgno iFrame;
-
-  assert( pWal->pWiData==0 );
-  rc = walIndexReadHdr(pWal, &unused);
-  for(iFrame=pWal->hdr.iLastPg+1; rc==SQLITE_OK && iFrame<=iMax; iFrame++){
-    assert( pWal->lockState==SQLITE_SHM_WRITE );
-    rc = xUndo(pUndoCtx, pWal->pWiData[walIndexEntry(iFrame)]);
+  int rc = SQLITE_OK;
+  if( pWal->lockState==SQLITE_SHM_WRITE ){
+    int unused;
+    Pgno iMax = pWal->hdr.iLastPg;
+    Pgno iFrame;
+  
+    assert( pWal->pWiData==0 );
+    rc = walIndexReadHdr(pWal, &unused);
+    for(iFrame=pWal->hdr.iLastPg+1; rc==SQLITE_OK && iFrame<=iMax; iFrame++){
+      assert( pWal->lockState==SQLITE_SHM_WRITE );
+      rc = xUndo(pUndoCtx, pWal->pWiData[walIndexEntry(iFrame)]);
+    }
+    walIndexUnmap(pWal);
   }
-  walIndexUnmap(pWal);
   return rc;
 }
 
@@ -1500,4 +1506,35 @@ int sqlite3WalCallback(Wal *pWal){
   }
   return (int)ret;
 }
+
+/*
+** This function is called to set or query the exclusive-mode flag 
+** associated with the WAL connection passed as the first argument. The
+** exclusive-mode flag should be set to indicate that the caller is
+** holding an EXCLUSIVE lock on the database file (it does this in
+** locking_mode=exclusive mode). If the EXCLUSIVE lock is to be dropped,
+** the flag set by this function should be cleared before doing so.
+**
+** The value of the exclusive-mode flag may only be modified when
+** the WAL connection is in READ state.
+**
+** When the flag is set, this module does not call the VFS xShmLock()
+** method to obtain any locks on the wal-index (as it assumes it
+** has exclusive access to the wal and wal-index files anyhow). It
+** continues to hold (and does not drop) the existing READ lock on
+** the wal-index.
+**
+** To set or clear the flag, the "op" parameter is passed 1 or 0,
+** respectively. To query the flag, pass -1. In all cases, the value
+** returned is the value of the exclusive-mode flag (after its value
+** has been modified, if applicable).
+*/
+int sqlite3WalExclusiveMode(Wal *pWal, int op){
+  if( op>=0 ){
+    assert( pWal->lockState==SQLITE_SHM_READ );
+    pWal->exclusiveMode = (u8)op;
+  }
+  return pWal->exclusiveMode;
+}
+
 #endif /* #ifndef SQLITE_OMIT_WAL */
index c1b662d09b98a46e2e54fed2a92c9e98d078bd46..89e94869c1cd48426fbddd816c8a56f26d5b94a4 100644 (file)
--- a/src/wal.h
+++ b/src/wal.h
@@ -96,5 +96,10 @@ int sqlite3WalCheckpoint(
 */
 int sqlite3WalCallback(Wal *pWal);
 
+/* Tell the wal layer that an EXCLUSIVE lock has been obtained (or released)
+** by the pager layer on the database file.
+*/
+int sqlite3WalExclusiveMode(Wal *pWal, int op);
+
 #endif /* ifndef SQLITE_OMIT_WAL */
 #endif /* _WAL_H_ */
index 81efbce82e2466427a196c9623f991f3abb3d198..e5ef2e07808143d93f86b725e8154a8d39884a9a 100644 (file)
@@ -362,8 +362,219 @@ do_test wal2-5.1 {
   execsql { PRAGMA wal_checkpoint }
   set ::locks
 } {CHECKPOINT UNLOCK}
-
 db close
 tvfs delete
 
+#-------------------------------------------------------------------------
+# This block, test cases wal2-6.*, tests the operation of WAL with
+# "PRAGMA locking_mode=EXCLUSIVE" set.
+#
+#   wal2-6.1.*: Changing to WAL mode before setting locking_mode=exclusive.
+#
+#   wal2-6.2.*: Changing to WAL mode after setting locking_mode=exclusive.
+#
+#   wal2-6.3.*: Changing back to rollback mode from WAL mode after setting 
+#               locking_mode=exclusive.
+#
+#   wal2-6.4.*: Check that xShmLock calls are omitted in exclusive locking
+#               mode.
+#
+do_test wal2-6.1.1 {
+  file delete -force test.db test.db-wal test.db-journal
+  sqlite3 db test.db
+  execsql {
+    Pragma Journal_Mode = Wal;
+    Pragma Locking_Mode = Exclusive;
+  }
+} {wal exclusive}
+do_test wal2-6.1.2 {
+  execsql { PRAGMA lock_status }
+} {main unlocked temp closed}
+do_test wal2-6.1.3 {
+  execsql {
+    BEGIN;
+      CREATE TABLE t1(a, b);
+      INSERT INTO t1 VALUES(1, 2);
+    COMMIT;
+    PRAGMA lock_status;
+  }
+} {main exclusive temp closed}
+do_test wal2-6.1.4 {
+  execsql { 
+    PRAGMA locking_mode = normal; 
+    PRAGMA lock_status;
+  }
+} {normal main exclusive temp closed}
+do_test wal2-6.1.5 {
+  execsql { 
+    SELECT * FROM t1;
+    PRAGMA lock_status;
+  }
+} {1 2 main exclusive temp closed}
+do_test wal2-6.1.6 {
+  execsql {
+    INSERT INTO t1 VALUES(3, 4);
+    PRAGMA lock_status;
+  }
+} {main shared temp closed}
+db close
+
+do_test wal2-6.2.1 {
+  file delete -force test.db test.db-wal test.db-journal
+  sqlite3 db test.db
+  execsql {
+    Pragma Locking_Mode = Exclusive;
+    Pragma Journal_Mode = Wal;
+    Pragma Lock_Status;
+  }
+} {exclusive wal main exclusive temp closed}
+do_test wal2-6.2.2 {
+  execsql {
+    BEGIN;
+      CREATE TABLE t1(a, b);
+      INSERT INTO t1 VALUES(1, 2);
+    COMMIT;
+    Pragma loCK_STATus;
+  }
+} {main exclusive temp closed}
+do_test wal2-6.2.3 {
+  db close
+  sqlite3 db test.db
+  execsql { PRAGMA LOCKING_MODE = EXCLUSIVE }
+} {exclusive}
+do_test wal2-6.2.4 {
+  execsql {
+    SELECT * FROM t1;
+    pragma lock_status;
+  }
+} {1 2 main shared temp closed}
+do_test wal2-6.2.5 {
+  execsql {
+    INSERT INTO t1 VALUES(3, 4);
+    pragma lock_status;
+  }
+} {main exclusive temp closed}
+do_test wal2-6.2.6 {
+  execsql {
+    PRAGMA locking_mode = NORMAL;
+    pragma lock_status;
+  }
+} {normal main exclusive temp closed}
+do_test wal2-6.2.7 {
+  execsql {
+    BEGIN IMMEDIATE; COMMIT;
+    pragma lock_status;
+  }
+} {main shared temp closed}
+do_test wal2-6.2.8 {
+  execsql {
+    PRAGMA locking_mode = EXCLUSIVE;
+    BEGIN IMMEDIATE; COMMIT;
+    PRAGMA locking_mode = NORMAL;
+  }
+  execsql {
+    SELECT * FROM t1;
+    pragma lock_status;
+  }
+} {1 2 3 4 main exclusive temp closed}
+do_test wal2-6.2.9 {
+  execsql {
+    INSERT INTO t1 VALUES(5, 6);
+    SELECT * FROM t1;
+    pragma lock_status;
+  }
+} {1 2 3 4 5 6 main shared temp closed}
+db close
+
+do_test wal2-6.3.1 {
+  file delete -force test.db test.db-wal test.db-journal
+  sqlite3 db test.db
+  execsql {
+    PRAGMA journal_mode = WAL;
+    PRAGMA locking_mode = exclusive;
+    BEGIN;
+      CREATE TABLE t1(x);
+      INSERT INTO t1 VALUES('Chico');
+      INSERT INTO t1 VALUES('Harpo');
+    COMMIT;
+  }
+  list [file exists test.db-wal] [file exists test.db-journal]
+} {1 0}
+do_test wal2-6.3.2 {
+  execsql { PRAGMA journal_mode = DELETE }
+  file exists test.db-wal
+} {0}
+do_test wal2-6.3.3 {
+  execsql { PRAGMA lock_status }
+} {main exclusive temp closed}
+do_test wal2-6.3.4 {
+  execsql { 
+    BEGIN;
+      INSERT INTO t1 VALUES('Groucho');
+  }
+  list [file exists test.db-wal] [file exists test.db-journal]
+} {0 1}
+do_test wal2-6.3.5 {
+  execsql { PRAGMA lock_status }
+} {main exclusive temp closed}
+do_test wal2-6.3.6 {
+  execsql { COMMIT }
+  list [file exists test.db-wal] [file exists test.db-journal]
+} {0 1}
+do_test wal2-6.3.7 {
+  execsql { PRAGMA lock_status }
+} {main exclusive temp closed}
+db close
+
+do_test wal2-6.4.1 {
+  file delete -force test.db test.db-wal test.db-journal
+  proc tvfs_cb {method args} {
+    set ::shm_file [lindex $args 0]
+    if {$method == "xShmLock"} { lappend ::locks [lindex $args 2] }
+    return "SQLITE_OK"
+  }
+  testvfs tvfs tvfs_cb
+  sqlite3 db test.db -vfs tvfs
+
+  execsql {
+    PRAGMA journal_mode = WAL;
+    CREATE TABLE t1(x);
+    INSERT INTO t1 VALUES('Leonard');
+    INSERT INTO t1 VALUES('Arthur');
+  }
+
+  set ::locks [list]
+  execsql { PRAGMA locking_mode = exclusive }
+  set ::locks
+} {}
+do_test wal2-6.4.2 {
+  execsql { SELECT * FROM t1 }
+} {Leonard Arthur}
+do_test wal2-6.4.3 {
+  set ::locks
+} {READ}
+do_test wal2-6.4.4 {
+  execsql { 
+    INSERT INTO t1 VALUES('Julius Henry');
+    SELECT * FROM t1;
+  }
+} {Leonard Arthur {Julius Henry}}
+do_test wal2-6.4.5 {
+  set ::locks
+} {READ}
+do_test wal2-6.4.6 {
+  execsql {
+    PRAGMA locking_mode = NORMAL;
+    DELETE FROM t1;
+  }
+  set ::locks
+} {READ UNLOCK}
+do_test wal2-6.4.7 {
+  set ::locks [list]
+  execsql { INSERT INTO t1 VALUES('Karl') }
+  set ::locks
+} {READ WRITE READ UNLOCK}
+db close
+
 finish_test
+