]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Experimental refactoring of the Pager object state.
authordan <dan@noemail.net>
Mon, 2 Aug 2010 14:32:52 +0000 (14:32 +0000)
committerdan <dan@noemail.net>
Mon, 2 Aug 2010 14:32:52 +0000 (14:32 +0000)
FossilOrigin-Name: 03a240514aa07a22db787d221641253f23933e88

manifest
manifest.uuid
src/pager.c
test/analyze3.test
test/exclusive.test
test/wal2.test

index fd0f62e526902e18470d24be8714c4bbcfff9ba8..d8a6e2a31d2759818bde625e25c013197b63298d 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Remove\svariable\sPager.needSync,\swhich\swas\salmost\scompletely\sunused.
-D 2010-07-30T15:43:13
+C Experimental\srefactoring\sof\sthe\sPager\sobject\sstate.
+D 2010-08-02T14:32:52
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in ec08dc838fd8110fe24c92e5130bcd91cbb1ff2e
 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -156,7 +156,7 @@ F src/os_common.h a8f95b81eca8a1ab8593d23e94f8a35f35d4078f
 F src/os_os2.c 72d0b2e562952a2464308c4ce5f7913ac10bef3e
 F src/os_unix.c ae5ca8a6031380708f3fec7be325233d49944914
 F src/os_win.c 51cb62f76262d961ea4249489383d714501315a7
-F src/pager.c 3e152b44733459c0a1f39d9a60646ab3aa4a7845
+F src/pager.c 94f00bedf9afb83c47f9b5d8bf8608f0eff088dd
 F src/pager.h 80726162dc3942f59ab27b738fb667b9ba0a89d5
 F src/parse.y 12b7ebd61ea54f0e1b1083ff69cc2c8ce9353d58
 F src/pcache.c 1e9aa2dbc0845b52e1b51cc39753b6d1e041cb07
@@ -241,7 +241,7 @@ F test/alter4.test 9386ffd1e9c7245f43eca412b2058d747509cc1f
 F test/altermalloc.test e81ac9657ed25c6c5bb09bebfa5a047cd8e4acfc
 F test/analyze.test bf692e7db414f268a136bade16c03a1bdbb9240c
 F test/analyze2.test 59dac6c399c0c5d1a90a11ee7cc606743fb6db93
-F test/analyze3.test 535bf0762f49fa96885efe8568738276c2204a2a
+F test/analyze3.test 6d4f4b0929545a9d1af803a0608a0c51b92a3537
 F test/async.test ad4ba51b77cd118911a3fe1356b0809da9c108c3
 F test/async2.test bf5e2ca2c96763b4cba3d016249ad7259a5603b6
 F test/async3.test 93edaa9122f498e56ea98c36c72abc407f4fb11e
@@ -347,7 +347,7 @@ F test/enc.test e54531cd6bf941ee6760be041dff19a104c7acea
 F test/enc2.test 6d91a5286f59add0cfcbb2d0da913b76f2242398
 F test/enc3.test 5c550d59ff31dccdba5d1a02ae11c7047d77c041
 F test/eval.test bc269c365ba877554948441e91ad5373f9f91be3
-F test/exclusive.test 5fe18e10a159342dd52ca14b1554e33f98734267
+F test/exclusive.test b1f9012cabc124af947165d15ffa62ad20f63db8
 F test/exclusive2.test fcbb1c9ca9739292a0a22a3763243ad6d868086b
 F test/exec.test e949714dc127eaa5ecc7d723efec1ec27118fdd7
 F test/expr.test 9f521ae22f00e074959f72ce2e55d46b9ed23f68
@@ -782,7 +782,7 @@ F test/vtab_alter.test 9e374885248f69e251bdaacf480b04a197f125e5
 F test/vtab_err.test 0d4d8eb4def1d053ac7c5050df3024fd47a3fbd8
 F test/vtab_shared.test 0eff9ce4f19facbe0a3e693f6c14b80711a4222d
 F test/wal.test 1891e6f72dd437a1c2a48091aa9182ba17a8f780
-F test/wal2.test fa6dc4457b46988f46cf6c68ea51ebe341765f4a
+F test/wal2.test 8581b133ef58d48f24c56f645a20d6527723557c
 F test/wal3.test 695ea0f6c516423c611891df9a285aacd33344e3
 F test/wal4.test 3404b048fa5e10605facaf70384e6d2943412e30
 F test/wal_common.tcl 895d76138043b86bdccf36494054bdabcf65837b
@@ -841,7 +841,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
 F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
 F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
-P 87e0f4e184284bf775c2fc7c4e9a334f4f237c59
-R 3e31f33b6e6cf5ef4a3978e597fbc7d4
+P 347f22a5b777af92873590a5b9af5a6498bef918
+R fb51b6835fc754e61c37a4fea5d6298b
 U dan
-Z 9b7608ba93c0b902202a5a1f630e015f
+Z 550ac78d14ba82c7a2ec704cc3fbe702
index d9b2afbfd1d6cc2eae446c0086dcb3aa95c00c3c..00992652061771c0ab441df1b83057aef8715832 100644 (file)
@@ -1 +1 @@
-347f22a5b777af92873590a5b9af5a6498bef918
\ No newline at end of file
+03a240514aa07a22db787d221641253f23933e88
\ No newline at end of file
index e3b590d5cade9fa17503bd4f9a46858f17c7a85e..947194927ffe914c003afcce2096a80c68ef02ce 100644 (file)
@@ -125,6 +125,72 @@ int sqlite3PagerTrace=1;  /* True to enable tracing */
 #define PAGERID(p) ((int)(p->fd))
 #define FILEHANDLEID(fd) ((int)fd)
 
+/*
+** The Pager.eState variable stores the current 'state' of a pager. A
+** pager may be in any one of the following six states:
+**
+**  NONE:
+**    * No read or write transaction is active.
+**    * Any lock, or no lock at all, may be held on the database file.
+**
+**  READER:
+**    * A read transaction is active.
+**    * A SHARED or greater lock is held on the database file.
+**    * The dbSize variable is valid.
+**
+**  WRITER_INITIAL:
+**    * A write transaction is active.
+**    * A RESERVED or greater lock is held on the database file.
+**    * The dbSize, dbOrigSize and dbFileSize variables are all valid.
+**    * The contents of the pager cache have not been modified.
+**
+**  WRITER_CACHEMOD:
+**    * A write transaction is active.
+**    * A RESERVED or greater lock is held on the database file.
+**    * The journal file is open and the first header has been written 
+**      to it, but the header has not been synced to disk.
+**    * The contents of the page cache have been modified.
+**
+**  WRITER_DBMOD:
+**    * A write transaction is active.
+**    * An EXCLUSIVE or greater lock is held on the database file.
+**    * The journal file is open and the first header has been written 
+**      and synced to disk.
+**    * The contents of the page cache have been modified (and possibly
+**      written to disk).
+**
+**  WRITER_FINISHED:
+**    * A write transaction is active.
+**    * An EXCLUSIVE or greater lock is held on the database file.
+**    * All writing and syncing of journal and database data has finished.
+**      If no error occured, all that remains is to finalize the journal to
+**      commit the transaction. If an error did occur, the caller will need
+**      to rollback the transaction. 
+**
+**
+** Allowable transitions and the [function] that performe each:
+** 
+**   NONE              -> READER              [PagerSharedLock]
+**   READER            -> WRITER_INITIAL      [PagerBegin]
+**   WRITER_INITIAL    -> WRITER_CACHEMOD     [pager_open_journal]
+**   WRITER_CACHEMOD   -> WRITER_DBMOD        [syncJournal]
+**   WRITER_DBMOD      -> WRITER_FINISHED     [PagerCommitPhaseOne]
+** 
+**   WRITER_INITIAL    -> READER              [pager_end_transaction]
+**   WRITER_CACHEMOD   -> READER              [pager_end_transaction]
+**   WRITER_DBMOD      -> READER              [pager_end_transaction]
+**   WRITER_FINISHED   -> READER              [pager_end_transaction]
+** 
+**   READER            -> NONE                [pager_unlock]
+*/
+#define PAGER_NONE                  0
+#define PAGER_READER                1
+#define PAGER_WRITER_INITIAL        2
+#define PAGER_WRITER_CACHEMOD       3
+#define PAGER_WRITER_DBMOD          4
+#define PAGER_WRITER_FINISHED       5
+
+
 /*
 ** The page cache as a whole is always in one of the following
 ** states:
@@ -355,14 +421,19 @@ struct Pager {
   ** other variables in this block are described in the comment directly 
   ** above this class definition.
   */
+#if 0
   u8 state;                   /* PAGER_UNLOCK, _SHARED, _RESERVED, etc. */
   u8 dbModified;              /* True if there are any changes to the Db */
   u8 journalStarted;          /* True if header of journal is synced */
+#endif
+  u8 dbSizeValid;             /* Set when dbSize is correct */
+  u8 eState;                  /* Pager state (NONE, READER, WRITER_INITIAL..) */
+  u8 eLock;                   /* Current lock held on database file */
+
   u8 changeCountDone;         /* Set after incrementing the change-counter */
   u8 setMaster;               /* True if a m-j name has been written to jrnl */
   u8 doNotSpill;              /* Do not spill the cache when non-zero */
   u8 doNotSyncSpill;          /* Do not do a spill that requires jrnl sync */
-  u8 dbSizeValid;             /* Set when dbSize is correct */
   u8 subjInMemory;            /* True to use in-memory sub-journals */
   Pgno dbSize;                /* Number of pages in the database */
   Pgno dbOrigSize;            /* dbSize before the current transaction */
@@ -484,22 +555,151 @@ static const unsigned char aJournalMagic[] = {
 */
 #define PAGER_MAX_PGNO 2147483647
 
+/*
+** The argument to this macro is a file descriptor (type sqlite3_file*).
+** Return 0 if it is not open, or non-zero (but not 1) if it is.
+**
+** This is so that expressions can be written as:
+**
+**   if( isOpen(pPager->jfd) ){ ...
+**
+** instead of
+**
+**   if( pPager->jfd->pMethods ){ ...
+*/
+#define isOpen(pFd) ((pFd)->pMethods)
+
+/*
+** Return true if this pager uses a write-ahead log instead of the usual
+** rollback journal. Otherwise false.
+*/
+#ifndef SQLITE_OMIT_WAL
+static int pagerUseWal(Pager *pPager){
+  return (pPager->pWal!=0);
+}
+#else
+# define pagerUseWal(x) 0
+# define pagerRollbackWal(x) 0
+# define pagerWalFrames(v,w,x,y,z) 0
+# define pagerOpenWalIfPresent(z) SQLITE_OK
+# define pagerBeginReadTransaction(z) SQLITE_OK
+#endif
+
 #ifndef NDEBUG 
 /*
 ** Usage:
 **
 **   assert( assert_pager_state(pPager) );
 */
-static int assert_pager_state(Pager *pPager){
+static int assert_pager_state(Pager *p){
+  Pager *pPager = p;
+
+  /* State must be valid. */
+  assert( p->eState==PAGER_NONE
+       || p->eState==PAGER_READER
+       || p->eState==PAGER_WRITER_INITIAL
+       || p->eState==PAGER_WRITER_CACHEMOD
+       || p->eState==PAGER_WRITER_DBMOD
+       || p->eState==PAGER_WRITER_FINISHED
+  );
+
+  /* Regardless of the current state, a temp-file connection always behaves
+  ** as if it has an exclusive lock on the database file. It never updates
+  ** the change-counter field, so the changeCountDone flag is always set.
+  */
+  assert( p->tempFile==0 || p->eLock==EXCLUSIVE_LOCK );
+  assert( p->tempFile==0 || pPager->changeCountDone );
+
+  /* If the useJournal flag is clear, the journal-mode must be "OFF". 
+  ** And if the journal-mode is "OFF", the journal file must not be open.
+  */
+  assert( p->journalMode==PAGER_JOURNALMODE_OFF || p->useJournal );
+  assert( p->journalMode!=PAGER_JOURNALMODE_OFF || !isOpen(p->jfd) );
+
+  /* Check that MEMDB implies noSync. */
+  assert( !MEMDB || p->noSync );
+
+  switch( p->eState ){
+    case PAGER_NONE:
+      assert( !MEMDB );
+      assert( !p->tempFile );
+      assert( !pagerUseWal(pPager) );
+      break;
+
+    case PAGER_READER:
+      assert( p->eLock>=SHARED_LOCK || p->noReadlock );
+      break;
+
+    case PAGER_WRITER_INITIAL:
+      if( !pagerUseWal(pPager) ){
+        assert( p->eLock>=RESERVED_LOCK );
+      }
+      break;
+
+    case PAGER_WRITER_CACHEMOD:
+      if( !pagerUseWal(pPager) ){
+        /* It is possible that if journal_mode=wal here that neither the
+        ** journal file nor the WAL file are open. This happens during
+        ** a rollback transaction that switches from journal_mode=off
+        ** to journal_mode=wal.
+        */
+        assert( p->eLock>=RESERVED_LOCK );
+        assert( isOpen(p->jfd) 
+             || p->journalMode==PAGER_JOURNALMODE_OFF 
+             || p->journalMode==PAGER_JOURNALMODE_WAL 
+        );
+      }
+      break;
 
-  /* A temp-file is always in PAGER_EXCLUSIVE or PAGER_SYNCED state. */
-  assert( pPager->tempFile==0 || pPager->state>=PAGER_EXCLUSIVE );
+    case PAGER_WRITER_DBMOD:
+      assert( !pagerUseWal(pPager) );
+      assert( p->eLock>=EXCLUSIVE_LOCK || pagerUseWal(pPager) );
+      assert( isOpen(p->jfd) 
+           || p->journalMode==PAGER_JOURNALMODE_OFF 
+           || p->journalMode==PAGER_JOURNALMODE_WAL 
+      );
+      break;
 
-  /* The changeCountDone flag is always set for temp-files */
-  assert( pPager->tempFile==0 || pPager->changeCountDone );
+    case PAGER_WRITER_FINISHED:
+      assert( !pagerUseWal(pPager) );
+      assert( p->eLock>=EXCLUSIVE_LOCK || pagerUseWal(pPager) );
+      assert( isOpen(p->jfd) 
+           || p->journalMode==PAGER_JOURNALMODE_OFF 
+           || p->journalMode==PAGER_JOURNALMODE_WAL 
+      );
+      break;
+  }
 
   return 1;
 }
+
+/*
+** (gdb) printf "%s", print_pager_state(pPager)
+*/
+static char *print_pager_state(Pager *p){
+  static char zRet[1024];
+
+  sqlite3_snprintf(1024, zRet,
+      "State: %s\n"
+      "Lock:  %s\n"
+      "Locking mode:  locking_mode=%s\n"
+      "Backing store: tempFile=%d memDb=%d\n"
+      , p->eState==PAGER_NONE            ? "NONE" :
+        p->eState==PAGER_READER          ? "READER" :
+        p->eState==PAGER_WRITER_INITIAL  ? "WRITER_INITIAL" :
+        p->eState==PAGER_WRITER_CACHEMOD ? "WRITER_CACHEMOD" :
+        p->eState==PAGER_WRITER_DBMOD    ? "WRITER_DBMOD" :
+        p->eState==PAGER_WRITER_FINISHED ? "WRITER_FINISHED" : "?error?"
+      , p->eLock==NO_LOCK         ? "NONE" :
+        p->eLock==RESERVED_LOCK   ? "RESERVED" :
+        p->eLock==EXCLUSIVE_LOCK  ? "EXCLUSIVE" :
+        p->eLock==SHARED_LOCK     ? "SHARED" : "?error?"
+      , p->exclusiveMode ? "exclusive" : "normal"
+      , (int)p->tempFile, (int)p->memDb
+  );
+
+  return zRet;
+}
 #endif
 
 /*
@@ -552,6 +752,7 @@ static int read32bits(sqlite3_file *fd, i64 offset, u32 *pRes){
 */
 #define put32bits(A,B)  sqlite3Put4byte((u8*)A,B)
 
+
 /*
 ** Write a 32-bit integer into the given file descriptor.  Return SQLITE_OK
 ** on success or an error code is something goes wrong.
@@ -562,28 +763,17 @@ static int write32bits(sqlite3_file *fd, i64 offset, u32 val){
   return sqlite3OsWrite(fd, ac, 4, offset);
 }
 
-/*
-** The argument to this macro is a file descriptor (type sqlite3_file*).
-** Return 0 if it is not open, or non-zero (but not 1) if it is.
-**
-** This is so that expressions can be written as:
-**
-**   if( isOpen(pPager->jfd) ){ ...
-**
-** instead of
-**
-**   if( pPager->jfd->pMethods ){ ...
-*/
-#define isOpen(pFd) ((pFd)->pMethods)
-
 /*
 ** If file pFd is open, call sqlite3OsUnlock() on it.
 */
-static int osUnlock(sqlite3_file *pFd, int eLock){
-  if( !isOpen(pFd) ){
+static int osUnlock(Pager *pPager, int eLock){
+  if( !isOpen(pPager->fd) ){
     return SQLITE_OK;
   }
-  return sqlite3OsUnlock(pFd, eLock);
+  assert( pPager->eLock>=eLock );
+  assert( eLock!=NO_LOCK || pagerUseWal(pPager)==0 );
+  pPager->eLock = eLock;
+  return sqlite3OsUnlock(pPager->fd, eLock);
 }
 
 /*
@@ -875,7 +1065,7 @@ static int writeJournalHdr(Pager *pPager){
   **     that garbage data is never appended to the journal file.
   */
   assert( isOpen(pPager->fd) || pPager->noSync );
-  if( (pPager->noSync) || (pPager->journalMode==PAGER_JOURNALMODE_MEMORY)
+  if( pPager->noSync || (pPager->journalMode==PAGER_JOURNALMODE_MEMORY)
    || (sqlite3OsDeviceCharacteristics(pPager->fd)&SQLITE_IOCAP_SAFE_APPEND) 
   ){
     memcpy(zHeader, aJournalMagic, sizeof(aJournalMagic));
@@ -1075,6 +1265,8 @@ static int writeMasterJournal(Pager *pPager, const char *zMaster){
   u32 cksum = 0;                   /* Checksum of string zMaster */
 
   assert( pPager->setMaster==0 );
+  assert( !zMaster || pPager->journalMode!=PAGER_JOURNALMODE_WAL );
+  assert( !pagerUseWal(pPager) );
 
   if( !zMaster 
    || pPager->journalMode==PAGER_JOURNALMODE_MEMORY 
@@ -1199,22 +1391,6 @@ static int addToSavepointBitvecs(Pager *pPager, Pgno pgno){
   return rc;
 }
 
-/*
-** Return true if this pager uses a write-ahead log instead of the usual
-** rollback journal. Otherwise false.
-*/
-#ifndef SQLITE_OMIT_WAL
-static int pagerUseWal(Pager *pPager){
-  return (pPager->pWal!=0);
-}
-#else
-# define pagerUseWal(x) 0
-# define pagerRollbackWal(x) 0
-# define pagerWalFrames(v,w,x,y,z) 0
-# define pagerOpenWalIfPresent(z) SQLITE_OK
-# define pagerBeginReadTransaction(z) SQLITE_OK
-#endif
-
 /*
 ** Unlock the database file. This function is a no-op if the pager
 ** is in exclusive mode.
@@ -1241,6 +1417,7 @@ static void pager_unlock(Pager *pPager){
     assert( (PAGER_JOURNALMODE_DELETE   & 5)!=1 );
     assert( (PAGER_JOURNALMODE_TRUNCATE & 5)==1 );
     assert( (PAGER_JOURNALMODE_PERSIST  & 5)==1 );
+
     if( 0==(iDc & SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN)
      || 1!=(pPager->journalMode & 5)
     ){
@@ -1262,7 +1439,7 @@ static void pager_unlock(Pager *pPager){
     if( pagerUseWal(pPager) ){
       sqlite3WalEndReadTransaction(pPager->pWal);
     }else{
-      rc = osUnlock(pPager->fd, NO_LOCK);
+      rc = osUnlock(pPager, NO_LOCK);
     }
     if( rc ){
       pPager->errCode = rc;
@@ -1281,8 +1458,7 @@ static void pager_unlock(Pager *pPager){
     }
 
     pPager->changeCountDone = 0;
-    pPager->state = PAGER_UNLOCK;
-    pPager->dbModified = 0;
+    pPager->eState = PAGER_NONE;
   }
 }
 
@@ -1319,32 +1495,6 @@ static int pager_error(Pager *pPager, int rc){
   return rc;
 }
 
-/*
-** Execute a rollback if a transaction is active and unlock the 
-** database file. 
-**
-** If the pager has already entered the error state, do not attempt 
-** the rollback at this time. Instead, pager_unlock() is called. The
-** call to pager_unlock() will discard all in-memory pages, unlock
-** the database file and clear the error state. If this means that
-** there is a hot-journal left in the file-system, the next connection
-** to obtain a shared lock on the pager (which may be this one) will
-** roll it back.
-**
-** If the pager has not already entered the error state, but an IO or
-** malloc error occurs during a rollback, then this will itself cause 
-** the pager to enter the error state. Which will be cleared by the
-** call to pager_unlock(), as described above.
-*/
-static void pagerUnlockAndRollback(Pager *pPager){
-  if( pPager->errCode==SQLITE_OK && pPager->state>=PAGER_RESERVED ){
-    sqlite3BeginBenignMalloc();
-    sqlite3PagerRollback(pPager);
-    sqlite3EndBenignMalloc();
-  }
-  pager_unlock(pPager);
-}
-
 /*
 ** This routine ends a transaction. A transaction is usually ended by 
 ** either a COMMIT or a ROLLBACK operation. This routine may be called 
@@ -1405,11 +1555,13 @@ static int pager_end_transaction(Pager *pPager, int hasMaster){
   int rc = SQLITE_OK;      /* Error code from journal finalization operation */
   int rc2 = SQLITE_OK;     /* Error code from db file unlock operation */
 
-  if( pPager->state<PAGER_RESERVED ){
+  /* Do nothing if the pager does not have an open write transaction. */
+  assert( assert_pager_state(pPager) );
+  if( pPager->eState<PAGER_WRITER_INITIAL && pPager->eLock<RESERVED_LOCK ){
     return SQLITE_OK;
   }
-  releaseAllSavepoints(pPager);
 
+  releaseAllSavepoints(pPager);
   assert( isOpen(pPager->jfd) || pPager->pInJournal==0 );
   if( isOpen(pPager->jfd) ){
     assert( !pagerUseWal(pPager) );
@@ -1425,14 +1577,12 @@ static int pager_end_transaction(Pager *pPager, int hasMaster){
         rc = sqlite3OsTruncate(pPager->jfd, 0);
       }
       pPager->journalOff = 0;
-      pPager->journalStarted = 0;
     }else if( pPager->journalMode==PAGER_JOURNALMODE_PERSIST
       || (pPager->exclusiveMode && pPager->journalMode!=PAGER_JOURNALMODE_WAL)
     ){
       rc = zeroJournalHdr(pPager, hasMaster);
       pager_error(pPager, rc);
       pPager->journalOff = 0;
-      pPager->journalStarted = 0;
     }else{
       /* This branch may be executed with Pager.journalMode==MEMORY if
       ** a hot-journal was just rolled back. In this case the journal
@@ -1457,39 +1607,58 @@ static int pager_end_transaction(Pager *pPager, int hasMaster){
   pPager->pInJournal = 0;
   pPager->nRec = 0;
   sqlite3PcacheCleanAll(pPager->pPCache);
+  sqlite3PcacheTruncate(pPager->pPCache, pPager->dbSize);
 
   if( pagerUseWal(pPager) ){
+    /* Drop the WAL write-lock, if any. Also, if the connection was in 
+    ** locking_mode=exclusive mode but is no longer, drop the EXCLUSIVE 
+    ** lock held on the database file.
+    */
     rc2 = sqlite3WalEndWriteTransaction(pPager->pWal);
     assert( rc2==SQLITE_OK );
-    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( !pPager->exclusiveMode && sqlite3WalExclusiveMode(pPager->pWal, 0) ){
-      rc2 = osUnlock(pPager->fd, SHARED_LOCK);
+      rc2 = osUnlock(pPager, SHARED_LOCK);
     }
   }else if( !pPager->exclusiveMode ){
-    rc2 = osUnlock(pPager->fd, SHARED_LOCK);
-    pPager->state = PAGER_SHARED;
+    rc2 = osUnlock(pPager, SHARED_LOCK);
     pPager->changeCountDone = 0;
-  }else if( pPager->state==PAGER_SYNCED ){
-    pPager->state = PAGER_EXCLUSIVE;
   }
+  pPager->eState = PAGER_READER;
   pPager->setMaster = 0;
-  pPager->dbModified = 0;
-
-  /* TODO: Is this optimal? Why is the db size invalidated here 
-  ** when the database file is not unlocked? */
-  pPager->dbOrigSize = 0;
-  sqlite3PcacheTruncate(pPager->pPCache, pPager->dbSize);
-  if( !MEMDB ){
-    pPager->dbSizeValid = 0;
-  }
 
   return (rc==SQLITE_OK?rc2:rc);
 }
 
+/*
+** Execute a rollback if a transaction is active and unlock the 
+** database file. 
+**
+** If the pager has already entered the error state, do not attempt 
+** the rollback at this time. Instead, pager_unlock() is called. The
+** call to pager_unlock() will discard all in-memory pages, unlock
+** the database file and clear the error state. If this means that
+** there is a hot-journal left in the file-system, the next connection
+** to obtain a shared lock on the pager (which may be this one) will
+** roll it back.
+**
+** If the pager has not already entered the error state, but an IO or
+** malloc error occurs during a rollback, then this will itself cause 
+** the pager to enter the error state. Which will be cleared by the
+** call to pager_unlock(), as described above.
+*/
+static void pagerUnlockAndRollback(Pager *pPager){
+  if( pPager->errCode==SQLITE_OK ){
+    if( pPager->eState>=PAGER_WRITER_INITIAL ){
+      sqlite3BeginBenignMalloc();
+      sqlite3PagerRollback(pPager);
+      sqlite3EndBenignMalloc();
+    }else if( pPager->eLock>=RESERVED_LOCK && !pPager->exclusiveMode ){
+      pager_end_transaction(pPager, 0);
+    }
+  }
+  pager_unlock(pPager);
+}
+
 /*
 ** Parameter aData must point to a buffer of pPager->pageSize bytes
 ** of data. Compute and return a checksum based ont the contents of the 
@@ -1632,7 +1801,7 @@ static int pager_playback_one_page(
   if( pDone && (rc = sqlite3BitvecSet(pDone, pgno))!=SQLITE_OK ){
     return rc;
   }
-  assert( pPager->state==PAGER_RESERVED || pPager->state>=PAGER_EXCLUSIVE );
+  assert( pPager->eState>=PAGER_WRITER_CACHEMOD );
 
   /* When playing back page 1, restore the nReserve setting
   */
@@ -1688,7 +1857,7 @@ static int pager_playback_one_page(
   }else{
     isSynced = (pPg==0 || 0==(pPg->flags & PGHDR_NEED_SYNC));
   }
-  if( (pPager->state>=PAGER_EXCLUSIVE)
+  if( (pPager->eState>=PAGER_WRITER_DBMOD)
    && isOpen(pPager->fd)
    && isSynced
   ){
@@ -1936,7 +2105,7 @@ delmaster_out:
 */
 static int pager_truncate(Pager *pPager, Pgno nPage){
   int rc = SQLITE_OK;
-  if( pPager->state>=PAGER_EXCLUSIVE && isOpen(pPager->fd) ){
+  if( pPager->eState>=PAGER_WRITER_DBMOD && isOpen(pPager->fd) ){
     i64 currentSize, newSize;
     /* TODO: Is it safe to use Pager.dbFileSize here? */
     rc = sqlite3OsFileSize(pPager->fd, &currentSize);
@@ -2056,10 +2225,13 @@ static int pager_playback(Pager *pPager, int isHot){
   char *zMaster = 0;       /* Name of master journal file if any */
   int needPagerReset;      /* True to reset page prior to first page rollback */
 
+  if( !isOpen(pPager->jfd) ){
+    return SQLITE_OK;
+  }
+
   /* Figure out how many records are in the journal.  Abort early if
   ** the journal is empty.
   */
-  assert( isOpen(pPager->jfd) );
   rc = sqlite3OsFileSize(pPager->jfd, &szJ);
   if( rc!=SQLITE_OK || szJ==0 ){
     goto end_playback;
@@ -2209,10 +2381,10 @@ end_playback:
     rc = readMasterJournal(pPager->jfd, zMaster, pPager->pVfs->mxPathname+1);
     testcase( rc!=SQLITE_OK );
   }
-  if( rc==SQLITE_OK && pPager->noSync==0 && pPager->state>=PAGER_EXCLUSIVE ){
+  if( rc==SQLITE_OK && !pPager->noSync && pPager->eState>=PAGER_WRITER_DBMOD ){
     rc = sqlite3OsSync(pPager->fd, pPager->sync_flags);
   }
-  if( rc==SQLITE_OK && pPager->noSync==0 && pPager->state>=PAGER_EXCLUSIVE ){
+  if( rc==SQLITE_OK && !pPager->noSync && pPager->eState>=PAGER_WRITER_DBMOD ){
     rc = sqlite3OsSync(pPager->fd, pPager->sync_flags);
   }
   if( rc==SQLITE_OK ){
@@ -2254,7 +2426,7 @@ static int readDbPage(PgHdr *pPg){
   int isInWal = 0;             /* True if page is in log file */
   int pgsz = pPager->pageSize; /* Number of bytes to read */
 
-  assert( pPager->state>=PAGER_SHARED && !MEMDB );
+  assert( pPager->eState>=PAGER_READER && !MEMDB );
   assert( isOpen(pPager->fd) );
 
   if( NEVER(!isOpen(pPager->fd)) ){
@@ -2434,7 +2606,7 @@ static int pagerBeginReadTransaction(Pager *pPager){
     }
     rc = sqlite3PagerPagecount(pPager, &dummy);
   }
-  pPager->state = PAGER_SHARED;
+  pPager->eState = PAGER_READER;
 
   return rc;
 }
@@ -2464,7 +2636,8 @@ static int pagerOpenWalIfPresent(Pager *pPager){
   if( !pPager->tempFile ){
     int isWal;                    /* True if WAL file exists */
     int nPage;                    /* Size of the database file */
-    assert( pPager->state>=SHARED_LOCK );
+
+    assert( pPager->eLock>=SHARED_LOCK || pPager->noReadlock );
     rc = sqlite3PagerPagecount(pPager, &nPage);
     if( rc ) return rc;
     if( nPage==0 ){
@@ -2533,7 +2706,7 @@ static int pagerPlaybackSavepoint(Pager *pPager, PagerSavepoint *pSavepoint){
   int rc = SQLITE_OK;      /* Return code */
   Bitvec *pDone = 0;       /* Bitvec to ensure pages played back only once */
 
-  assert( pPager->state>=PAGER_SHARED );
+  assert( pPager->eState>=PAGER_WRITER_INITIAL );
 
   /* Allocate a bitvec to use to store the set of pages rolled back */
   if( pSavepoint ){
@@ -2831,7 +3004,7 @@ int sqlite3PagerMaxPageCount(Pager *pPager, int mxPage){
   if( mxPage>0 ){
     pPager->mxPgno = mxPage;
   }
-  if( pPager->state!=PAGER_UNLOCK ){
+  if( pPager->eState!=PAGER_NONE ){
     sqlite3PagerPagecount(pPager, &nPage);
     assert( (int)pPager->mxPgno>=nPage );
   }
@@ -2922,7 +3095,7 @@ int sqlite3PagerPagecount(Pager *pPager, int *pnPage){
     int rc;                 /* Error returned by OsFileSize() */
     i64 n = 0;              /* File size in bytes returned by OsFileSize() */
 
-    if( pagerUseWal(pPager) && pPager->state!=PAGER_UNLOCK ){
+    if( pagerUseWal(pPager) && pPager->eState!=PAGER_NONE ){
       sqlite3WalDbsize(pPager->pWal, &nPage);
     }
 
@@ -2940,7 +3113,7 @@ int sqlite3PagerPagecount(Pager *pPager, int *pnPage){
         nPage = (Pgno)(n / pPager->pageSize);
       }
     }
-    if( pPager->state!=PAGER_UNLOCK ){
+    if( pPager->eState!=PAGER_NONE ){
       pPager->dbSize = nPage;
       pPager->dbFileSize = nPage;
       pPager->dbSizeValid = 1;
@@ -2961,6 +3134,20 @@ int sqlite3PagerPagecount(Pager *pPager, int *pnPage){
 }
 
 
+static int pagerLock(Pager *pPager, int eLock){
+  int rc;
+  assert( eLock==SHARED_LOCK || eLock==RESERVED_LOCK || eLock==EXCLUSIVE_LOCK );
+  if( pPager->eLock>=eLock ){
+    rc = SQLITE_OK;
+  }else{
+    rc = sqlite3OsLock(pPager->fd, eLock);
+    if( rc==SQLITE_OK ){
+      pPager->eLock = eLock;
+    }
+  }
+  return rc;
+}
+
 /*
 ** Try to obtain a lock of type locktype on the database file. If
 ** a similar or greater lock is already held, this function is a no-op
@@ -2983,30 +3170,24 @@ static int pager_wait_on_lock(Pager *pPager, int locktype){
   assert( PAGER_RESERVED==RESERVED_LOCK );
   assert( PAGER_EXCLUSIVE==EXCLUSIVE_LOCK );
 
-  /* If the file is currently unlocked then the size must be unknown. It
-  ** must not have been modified at this point.
-  */
-  assert( pPager->state>=PAGER_SHARED || pPager->dbSizeValid==0 );
-  assert( pPager->state>=PAGER_SHARED || pPager->dbModified==0 );
-
   /* Check that this is either a no-op (because the requested lock is 
   ** already held, or one of the transistions that the busy-handler
   ** may be invoked during, according to the comment above
   ** sqlite3PagerSetBusyhandler().
   */
-  assert( (pPager->state>=locktype)
-       || (pPager->state==PAGER_UNLOCK && locktype==PAGER_SHARED)
-       || (pPager->state==PAGER_RESERVED && locktype==PAGER_EXCLUSIVE)
+  assert( (pPager->eLock>=locktype)
+       || (pPager->eLock==NO_LOCK && locktype==SHARED_LOCK)
+       || (pPager->eLock==RESERVED_LOCK && locktype==EXCLUSIVE_LOCK)
   );
 
-  if( pPager->state>=locktype ){
+  if( pPager->eLock>=locktype ){
     rc = SQLITE_OK;
   }else{
     do {
       rc = sqlite3OsLock(pPager->fd, locktype);
     }while( rc==SQLITE_BUSY && pPager->xBusyHandler(pPager->pBusyHandlerArg) );
     if( rc==SQLITE_OK ){
-      pPager->state = (u8)locktype;
+      pPager->eLock = (u8)locktype;
       IOTRACE(("LOCK %p %d\n", pPager, locktype))
     }
   }
@@ -3056,7 +3237,7 @@ static void assertTruncateConstraint(Pager *pPager){
 void sqlite3PagerTruncateImage(Pager *pPager, Pgno nPage){
   assert( pPager->dbSizeValid );
   assert( pPager->dbSize>=nPage );
-  assert( pPager->state>=PAGER_RESERVED );
+  assert( pPager->eState>=PAGER_WRITER_CACHEMOD );
   pPager->dbSize = nPage;
   assertTruncateConstraint(pPager);
 }
@@ -3202,12 +3383,19 @@ void sqlite3PagerRef(DbPage *pPg){
 ** error is encountered, then the IO error code is returned to the caller.
 */
 static int syncJournal(Pager *pPager){
+  int rc;                         /* Return code */
+
+  assert( pPager->eState==PAGER_WRITER_CACHEMOD
+       || pPager->eState==PAGER_WRITER_DBMOD
+  );
+  assert( assert_pager_state(pPager) );
+
+  rc = sqlite3PagerExclusiveLock(pPager);
+  if( rc!=SQLITE_OK ) return rc;
+
   if( !pPager->noSync ){
     assert( !pPager->tempFile );
-    if( pPager->journalMode!=PAGER_JOURNALMODE_MEMORY 
-     && pPager->journalMode!=PAGER_JOURNALMODE_OFF 
-    ){
-      int rc;                              /* Return code */
+    if( isOpen(pPager->jfd) && pPager->journalMode!=PAGER_JOURNALMODE_MEMORY ){
       const int iDc = sqlite3OsDeviceCharacteristics(pPager->fd);
       assert( isOpen(pPager->jfd) );
 
@@ -3284,14 +3472,16 @@ static int syncJournal(Pager *pPager){
       }
     }
 
-    /* The journal file was just successfully synced. Clear the 
-    ** PGHDR_NEED_SYNC flag on all pagess.
-    */
-    pPager->journalStarted = 1;
     pPager->journalHdr = pPager->journalOff;
-    sqlite3PcacheClearSyncFlags(pPager->pPCache);
   }
 
+  /* Unless the pager is in noSync mode, the journal file was just 
+  ** successfully synced. Either way, clear the PGHDR_NEED_SYNC flag on 
+  ** all pages.
+  */
+  sqlite3PcacheClearSyncFlags(pPager->pPCache);
+  pPager->eState = PAGER_WRITER_DBMOD;
+  assert( assert_pager_state(pPager) );
   return SQLITE_OK;
 }
 
@@ -3346,8 +3536,21 @@ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){
   ** EXCLUSIVE, it means the database file has been changed and any rollback
   ** will require a journal playback.
   */
+
+  /* Normally, this function is called in WRITER_DBMOD state.
+  **
+  ** However it may be called in WRITER_CACHEMOD state if the page being
+  ** written (and all other pages that reside on the same disk sector) was
+  ** a free-list leaf page at the start of the transaction. In that case
+  ** the database file is not really being modified, so it is Ok to write
+  ** to it in CACHEMOD state.
+  */
   assert( !pagerUseWal(pPager) );
-  assert( pPager->state>=PAGER_RESERVED );
+  assert( pPager->eState==PAGER_WRITER_CACHEMOD
+       || pPager->eState==PAGER_WRITER_DBMOD 
+  );
+  assert( pPager->eState==PAGER_WRITER_DBMOD || pList->pDirty==0 );
+
   rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
 
   /* If the file is a temp-file has not yet been opened, open it now. It
@@ -3548,9 +3751,9 @@ static int pagerStress(void *p, PgHdr *pPg){
   
     /* Sync the journal file if required. */
     if( pPg->flags&PGHDR_NEED_SYNC ){
-      assert( !pPager->noSync );
       rc = syncJournal(pPager);
       if( rc==SQLITE_OK && 
+        !pPager->noSync &&
         !(pPager->journalMode==PAGER_JOURNALMODE_MEMORY) &&
         !(sqlite3OsDeviceCharacteristics(pPager->fd)&SQLITE_IOCAP_SAFE_APPEND)
       ){
@@ -3823,7 +4026,8 @@ int sqlite3PagerOpen(
     ** disk and uses an in-memory rollback journal.
     */ 
     tempFile = 1;
-    pPager->state = PAGER_EXCLUSIVE;
+    pPager->eState = PAGER_READER;
+    pPager->eLock = EXCLUSIVE_LOCK;
     readOnly = (vfsFlags&SQLITE_OPEN_READONLY);
   }
 
@@ -3866,7 +4070,9 @@ int sqlite3PagerOpen(
   /* pPager->nPage = 0; */
   pPager->mxPgno = SQLITE_MAX_PAGE_COUNT;
   /* pPager->state = PAGER_UNLOCK; */
+#if 0
   assert( pPager->state == (tempFile ? PAGER_EXCLUSIVE : PAGER_UNLOCK) );
+#endif
   /* pPager->errMask = 0; */
   pPager->tempFile = (u8)tempFile;
   assert( tempFile==PAGER_LOCKINGMODE_NORMAL 
@@ -3943,7 +4149,8 @@ static int hasHotJournal(Pager *pPager, int *pExists){
   assert( pPager!=0 );
   assert( pPager->useJournal );
   assert( isOpen(pPager->fd) );
-  assert( pPager->state <= PAGER_SHARED );
+  assert( pPager->eState<=PAGER_SHARED );
+
   assert( jrnlOpen==0 || ( sqlite3OsDeviceCharacteristics(pPager->jfd) &
     SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN
   ));
@@ -4079,28 +4286,27 @@ int sqlite3PagerSharedLock(Pager *pPager){
 
   if( pagerUseWal(pPager) ){
     rc = pagerBeginReadTransaction(pPager);
-  }else if( pPager->state==PAGER_UNLOCK || isErrorReset ){
+  }else if( pPager->eState==PAGER_NONE || isErrorReset ){
     sqlite3_vfs * const pVfs = pPager->pVfs;
     int isHotJournal = 0;
-    assert( !MEMDB );
+
+    assert( !MEMDB && !pPager->tempFile );
     assert( sqlite3PcacheRefCount(pPager->pPCache)==0 );
-    if( pPager->noReadlock ){
-      assert( pPager->readOnly );
-      pPager->state = PAGER_SHARED;
-    }else{
+    assert( pPager->noReadlock==0 || pPager->readOnly );
+
+    if( pPager->noReadlock==0 ){
       rc = pager_wait_on_lock(pPager, SHARED_LOCK);
       if( rc!=SQLITE_OK ){
-        assert( pPager->state==PAGER_UNLOCK );
+        assert( pPager->eLock==PAGER_UNLOCK );
         return pager_error(pPager, rc);
       }
     }
-    assert( pPager->state>=SHARED_LOCK );
+    pPager->eState = PAGER_READER;
 
     /* If a journal file exists, and there is no RESERVED lock on the
     ** database file, then it either needs to be played back or deleted.
     */
     if( !isErrorReset ){
-      assert( pPager->state <= PAGER_SHARED );
       rc = hasHotJournal(pPager, &isHotJournal);
       if( rc!=SQLITE_OK ){
         goto failed;
@@ -4118,21 +4324,28 @@ int sqlite3PagerSharedLock(Pager *pPager){
       ** other process attempting to access the database file will get to 
       ** this point in the code and fail to obtain its own EXCLUSIVE lock 
       ** on the database file.
+      **
+      ** Unless the pager is in locking_mode=exclusive mode, the lock is
+      ** downgraded to SHARED_LOCK before this function returns.
       */
-      if( pPager->state<EXCLUSIVE_LOCK ){
-        rc = sqlite3OsLock(pPager->fd, EXCLUSIVE_LOCK);
-        if( rc!=SQLITE_OK ){
-          rc = pager_error(pPager, rc);
-          goto failed;
-        }
-        pPager->state = PAGER_EXCLUSIVE;
+      rc = pagerLock(pPager, EXCLUSIVE_LOCK);
+      if( rc!=SQLITE_OK ){
+        rc = pager_error(pPager, rc);
+        goto failed;
       }
  
-      /* Open the journal for read/write access. This is because in 
-      ** exclusive-access mode the file descriptor will be kept open and
-      ** possibly used for a transaction later on. On some systems, the
-      ** OsTruncate() call used in exclusive-access mode also requires
-      ** a read/write file handle.
+      /* If it is not already open and the file exists on disk, open the 
+      ** journal for read/write access. Write access is required because 
+      ** in exclusive-access mode the file descriptor will be kept open 
+      ** and possibly used for a transaction later on. Also, write-access 
+      ** is usually required to finalize the journal in journal_mode=persist 
+      ** mode (and also for journal_mode=truncate on some systems).
+      **
+      ** If the journal does not exist, it usually means that some 
+      ** other connection managed to get in and roll it back before 
+      ** this connection obtained the exclusive lock above. Or, it 
+      ** may mean that the pager was in the error-state when this
+      ** function was called and the journal file does not exist.
       */
       if( !isOpen(pPager->jfd) ){
         int res;
@@ -4148,13 +4361,6 @@ int sqlite3PagerSharedLock(Pager *pPager){
               rc = SQLITE_CANTOPEN_BKPT;
               sqlite3OsClose(pPager->jfd);
             }
-          }else{
-            /* If the journal does not exist, it usually means that some 
-            ** other connection managed to get in and roll it back before 
-            ** this connection obtained the exclusive lock above. Or, it 
-            ** may mean that the pager was in the error-state when this
-            ** function was called and the journal file does not exist.  */
-            rc = pager_end_transaction(pPager, 0);
           }
         }
       }
@@ -4164,7 +4370,6 @@ int sqlite3PagerSharedLock(Pager *pPager){
 
       /* Reset the journal status fields to indicates that we have no
       ** rollback journal at this time. */
-      pPager->journalStarted = 0;
       pPager->journalOff = 0;
       pPager->setMaster = 0;
       pPager->journalHdr = 0;
@@ -4182,15 +4387,21 @@ int sqlite3PagerSharedLock(Pager *pPager){
       if( isOpen(pPager->jfd) ){
         rc = pagerSyncHotJournal(pPager);
         if( rc==SQLITE_OK ){
+          pPager->eState = PAGER_WRITER_FINISHED;
           rc = pager_playback(pPager, 1);
+          pPager->eState = PAGER_READER;
         }
         if( rc!=SQLITE_OK ){
           rc = pager_error(pPager, rc);
           goto failed;
         }
+      }else{
+        osUnlock(pPager, SHARED_LOCK);
       }
-      assert( (pPager->state==PAGER_SHARED)
-           || (pPager->exclusiveMode && pPager->state>PAGER_SHARED)
+
+      assert( pPager->eState==PAGER_READER );
+      assert( (pPager->eLock==SHARED_LOCK)
+           || (pPager->exclusiveMode && pPager->eLock>SHARED_LOCK)
       );
     }
 
@@ -4234,7 +4445,7 @@ int sqlite3PagerSharedLock(Pager *pPager){
         pager_reset(pPager);
       }
     }
-    assert( pPager->exclusiveMode || pPager->state==PAGER_SHARED );
+    assert( pPager->eState==PAGER_READER );
 
     /* If there is a WAL file in the file-system, open this database in WAL
     ** mode. Otherwise, the following function call is a no-op.
@@ -4325,8 +4536,8 @@ int sqlite3PagerAcquire(
   int rc;
   PgHdr *pPg;
 
+  assert( pPager->eState>=PAGER_READER );
   assert( assert_pager_state(pPager) );
-  assert( pPager->state>PAGER_UNLOCK );
 
   if( pgno==0 ){
     return SQLITE_CORRUPT_BKPT;
@@ -4444,7 +4655,7 @@ DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno){
   assert( pPager!=0 );
   assert( pgno!=0 );
   assert( pPager->pPCache!=0 );
-  assert( pPager->state > PAGER_UNLOCK );
+  assert( pPager->eState>=PAGER_READER );
   sqlite3PcacheFetch(pPager->pPCache, pgno, 0, &pPg);
   return pPg;
 }
@@ -4492,9 +4703,8 @@ static int pager_open_journal(Pager *pPager){
   int nPage;                                 /* Size of database file */
   sqlite3_vfs * const pVfs = pPager->pVfs;   /* Local cache of vfs pointer */
 
-  assert( pPager->state>=PAGER_RESERVED );
-  assert( pPager->useJournal );
-  assert( pPager->journalMode!=PAGER_JOURNALMODE_OFF );
+  assert( pPager->eState==PAGER_WRITER_INITIAL );
+  assert( assert_pager_state(pPager) );
   assert( pPager->pInJournal==0 );
   
   /* If already in the error state, this function is a no-op.  But on
@@ -4502,55 +4712,59 @@ static int pager_open_journal(Pager *pPager){
   ** an error state. */
   if( NEVER(pPager->errCode) ) return pPager->errCode;
 
-  testcase( pPager->dbSizeValid==0 );
-  rc = sqlite3PagerPagecount(pPager, &nPage);
-  if( rc ) return rc;
-  pPager->pInJournal = sqlite3BitvecCreate(nPage);
-  if( pPager->pInJournal==0 ){
-    return SQLITE_NOMEM;
-  }
-
-  /* Open the journal file if it is not already open. */
-  if( !isOpen(pPager->jfd) ){
-    if( pPager->journalMode==PAGER_JOURNALMODE_MEMORY ){
-      sqlite3MemJournalOpen(pPager->jfd);
-    }else{
-      const int flags =                   /* VFS flags to open journal file */
-        SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|
-        (pPager->tempFile ? 
-          (SQLITE_OPEN_DELETEONCLOSE|SQLITE_OPEN_TEMP_JOURNAL):
-          (SQLITE_OPEN_MAIN_JOURNAL)
+  if( !pagerUseWal(pPager) && pPager->journalMode!=PAGER_JOURNALMODE_OFF ){
+    testcase( pPager->dbSizeValid==0 );
+    rc = sqlite3PagerPagecount(pPager, &nPage);
+    if( rc ) return rc;
+    pPager->pInJournal = sqlite3BitvecCreate(nPage);
+    if( pPager->pInJournal==0 ){
+      return SQLITE_NOMEM;
+    }
+  
+    /* Open the journal file if it is not already open. */
+    if( !isOpen(pPager->jfd) ){
+      if( pPager->journalMode==PAGER_JOURNALMODE_MEMORY ){
+        sqlite3MemJournalOpen(pPager->jfd);
+      }else{
+        const int flags =                   /* VFS flags to open journal file */
+          SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|
+          (pPager->tempFile ? 
+            (SQLITE_OPEN_DELETEONCLOSE|SQLITE_OPEN_TEMP_JOURNAL):
+            (SQLITE_OPEN_MAIN_JOURNAL)
+          );
+  #ifdef SQLITE_ENABLE_ATOMIC_WRITE
+        rc = sqlite3JournalOpen(
+            pVfs, pPager->zJournal, pPager->jfd, flags, jrnlBufferSize(pPager)
         );
-#ifdef SQLITE_ENABLE_ATOMIC_WRITE
-      rc = sqlite3JournalOpen(
-          pVfs, pPager->zJournal, pPager->jfd, flags, jrnlBufferSize(pPager)
-      );
-#else
-      rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, flags, 0);
-#endif
+  #else
+        rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, flags, 0);
+  #endif
+      }
+      assert( rc!=SQLITE_OK || isOpen(pPager->jfd) );
+    }
+  
+  
+    /* Write the first journal header to the journal file and open 
+    ** the sub-journal if necessary.
+    */
+    if( rc==SQLITE_OK ){
+      /* TODO: Check if all of these are really required. */
+      pPager->dbOrigSize = pPager->dbSize;
+      pPager->nRec = 0;
+      pPager->journalOff = 0;
+      pPager->setMaster = 0;
+      pPager->journalHdr = 0;
+      rc = writeJournalHdr(pPager);
     }
-    assert( rc!=SQLITE_OK || isOpen(pPager->jfd) );
-  }
-
-
-  /* Write the first journal header to the journal file and open 
-  ** the sub-journal if necessary.
-  */
-  if( rc==SQLITE_OK ){
-    /* TODO: Check if all of these are really required. */
-    pPager->dbOrigSize = pPager->dbSize;
-    pPager->journalStarted = 0;
-    pPager->nRec = 0;
-    pPager->journalOff = 0;
-    pPager->setMaster = 0;
-    pPager->journalHdr = 0;
-    rc = writeJournalHdr(pPager);
   }
 
   if( rc!=SQLITE_OK ){
     sqlite3BitvecDestroy(pPager->pInJournal);
     pPager->pInJournal = 0;
+  }else{
+    pPager->eState = PAGER_WRITER_CACHEMOD;
   }
+
   return rc;
 }
 
@@ -4581,20 +4795,18 @@ static int pager_open_journal(Pager *pPager){
 */
 int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory){
   int rc = SQLITE_OK;
-  assert( pPager->state!=PAGER_UNLOCK );
+  assert( pPager->eState>=PAGER_READER );
   pPager->subjInMemory = (u8)subjInMemory;
 
-  if( pPager->state==PAGER_SHARED ){
+  if( pPager->eState==PAGER_READER ){
     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;
+        rc = pagerLock(pPager, EXCLUSIVE_LOCK);
         if( rc!=SQLITE_OK ){
           return rc;
         }
@@ -4605,45 +4817,35 @@ int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory){
       ** 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 = sqlite3WalBeginWriteTransaction(pPager->pWal);
-      if( rc==SQLITE_OK ){
-        pPager->dbOrigSize = pPager->dbSize;
-        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
       ** busy-handler callback can be used when upgrading to the EXCLUSIVE
       ** lock, but not when obtaining the RESERVED lock.
       */
-      rc = sqlite3OsLock(pPager->fd, RESERVED_LOCK);
-      if( rc==SQLITE_OK ){
-        pPager->state = PAGER_RESERVED;
-        if( exFlag ){
-          rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
-        }
+      rc = pagerLock(pPager, RESERVED_LOCK);
+      if( rc==SQLITE_OK && exFlag ){
+        rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
       }
     }
 
-    /* No need to open the journal file at this time.  It will be
-    ** opened before it is written to.  If we defer opening the journal,
-    ** we might save the work of creating a file if the transaction
-    ** ends up being a no-op.
-    */
 
-    if( rc!=SQLITE_OK ){
-      assert( !pPager->dbModified );
+    if( rc==SQLITE_OK ){
+      /* Change to WRITER_INITIAL state.
+      **
+      ** WAL mode sets Pager.eState to PAGER_WRITER_INITIAL or CACHEMOD
+      ** when it has an open transaction, but never to DBMOD or FINISHED.
+      ** This is because in those states 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.
+      */
+      pPager->eState = PAGER_WRITER_INITIAL;
+      pPager->dbOrigSize = pPager->dbSize;
+      pPager->journalOff = 0;
+    }else{
       /* Ignore any IO error that occurs within pager_end_transaction(). The
       ** purpose of this call is to reset the internal state of the pager
       ** sub-system. It doesn't matter if the journal-file is not properly
@@ -4651,6 +4853,10 @@ int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory){
       */
       pager_end_transaction(pPager, 0);
     }
+
+    assert( rc==SQLITE_OK || pPager->eState==PAGER_READER );
+    assert( rc!=SQLITE_OK || pPager->eState==PAGER_WRITER_INITIAL );
+    assert( assert_pager_state(pPager) );
   }
 
   PAGERTRACE(("TRANSACTION %d\n", PAGERID(pPager)));
@@ -4669,10 +4875,12 @@ static int pager_write(PgHdr *pPg){
   Pager *pPager = pPg->pPager;
   int rc = SQLITE_OK;
 
-  /* This routine is not called unless a transaction has already been
-  ** started.
+  /* This routine is not called unless a write-transaction has already 
+  ** been started. The journal file may or may not be open at this point.
   */
-  assert( pPager->state>=PAGER_RESERVED );
+  assert( pPager->eState>=PAGER_WRITER_INITIAL );
+  assert( pPager->eState!=PAGER_WRITER_FINISHED );
+  assert( assert_pager_state(pPager) );
 
   /* If an error has been previously detected, report the same error
   ** again.
@@ -4683,8 +4891,6 @@ static int pager_write(PgHdr *pPg){
   ** writable.  But check anyway, just for robustness. */
   if( NEVER(pPager->readOnly) ) return SQLITE_PERM;
 
-  assert( !pPager->setMaster );
-
   CHECK_PAGE(pPg);
 
   /* Mark the page as dirty.  If the page has already been written
@@ -4693,27 +4899,22 @@ static int pager_write(PgHdr *pPg){
   sqlite3PcacheMakeDirty(pPg);
   if( pageInJournal(pPg) && !subjRequiresPage(pPg) ){
     assert( !pagerUseWal(pPager) );
-    pPager->dbModified = 1;
+    assert( pPager->eState>=PAGER_WRITER_CACHEMOD );
   }else{
 
     /* If we get this far, it means that the page needs to be
-    ** written to the transaction journal or the ckeckpoint journal
+    ** written to the transaction journal or the checkpoint journal
     ** or both.
     **
-    ** Higher level routines should have already started a transaction,
-    ** which means they have acquired the necessary locks but the rollback
-    ** journal might not yet be open.
+    ** Higher level routines have already obtained the necessary locks
+    ** to begin the write-transaction, but the rollback journal might not 
+    ** yet be open. Open it now if this is the case.
     */
-    assert( pPager->state>=RESERVED_LOCK );
-    if( pPager->pInJournal==0
-     && pPager->journalMode!=PAGER_JOURNALMODE_OFF 
-     && !pagerUseWal(pPager)
-    ){
-      assert( pPager->useJournal );
+    if( pPager->eState==PAGER_WRITER_INITIAL ){
       rc = pager_open_journal(pPager);
       if( rc!=SQLITE_OK ) return rc;
     }
-    pPager->dbModified = 1;
+    assert( pPager->eState>=PAGER_WRITER_CACHEMOD );
   
     /* The transaction journal now exists and we have a RESERVED or an
     ** EXCLUSIVE lock on the main database file.  Write the current page to
@@ -4757,7 +4958,7 @@ static int pager_write(PgHdr *pPg){
         ** in the database file. And if an IO error occurs while doing so,
         ** then corruption may follow.
         */
-        if( !pPager->noSync ){
+        if( 1 || !pPager->noSync ){
           pPg->flags |= PGHDR_NEED_SYNC;
         }
 
@@ -4779,7 +4980,7 @@ static int pager_write(PgHdr *pPg){
           return rc;
         }
       }else{
-        if( !pPager->journalStarted && !pPager->noSync ){
+        if( pPager->eState!=PAGER_WRITER_DBMOD && (!pPager->noSync || 1)){
           pPg->flags |= PGHDR_NEED_SYNC;
         }
         PAGERTRACE(("APPEND %d page %d needSync=%d\n",
@@ -4800,7 +5001,6 @@ static int pager_write(PgHdr *pPg){
 
   /* Update the database size and return.
   */
-  assert( pPager->state>=PAGER_SHARED );
   if( pPager->dbSize<pPg->pgno ){
     pPager->dbSize = pPg->pgno;
   }
@@ -4966,6 +5166,11 @@ void sqlite3PagerDontWrite(PgHdr *pPg){
 static int pager_incr_changecounter(Pager *pPager, int isDirectMode){
   int rc = SQLITE_OK;
 
+  assert( pPager->eState==PAGER_WRITER_CACHEMOD
+       || pPager->eState==PAGER_WRITER_DBMOD
+  );
+  assert( assert_pager_state(pPager) );
+
   /* Declare and initialize constant integer 'isDirect'. If the
   ** atomic-write optimization is enabled in this build, then isDirect
   ** is initialized to the value passed as the isDirectMode parameter
@@ -4984,7 +5189,6 @@ static int pager_incr_changecounter(Pager *pPager, int isDirectMode){
 # define DIRECT_MODE isDirectMode
 #endif
 
-  assert( pPager->state>=PAGER_RESERVED );
   if( !pPager->changeCountDone && pPager->dbSize>0 ){
     PgHdr *pPgHdr;                /* Reference to page 1 */
     u32 change_counter;           /* Initial value of change-counter field */
@@ -5069,7 +5273,11 @@ int sqlite3PagerSync(Pager *pPager){
 */
 int sqlite3PagerExclusiveLock(Pager *pPager){
   int rc = SQLITE_OK;
-  assert( pPager->state>=PAGER_RESERVED );
+  assert( pPager->eState==PAGER_WRITER_CACHEMOD 
+       || pPager->eState==PAGER_WRITER_DBMOD 
+       || pPager->eState==PAGER_WRITER_INITIAL 
+  );
+  assert( assert_pager_state(pPager) );
   if( 0==pagerUseWal(pPager) ){
     rc = pager_wait_on_lock(pPager, PAGER_EXCLUSIVE);
   }
@@ -5109,8 +5317,16 @@ int sqlite3PagerCommitPhaseOne(
 ){
   int rc = SQLITE_OK;             /* Return code */
 
+  assert( pPager->eState==PAGER_WRITER_INITIAL
+       || pPager->eState==PAGER_WRITER_CACHEMOD
+       || pPager->eState==PAGER_WRITER_DBMOD
+  );
+  assert( assert_pager_state(pPager) );
+
+#if 0
   /* The dbOrigSize is never set if journal_mode=OFF */
   assert( pPager->journalMode!=PAGER_JOURNALMODE_OFF || pPager->dbOrigSize==0 );
+#endif
 
   /* If a prior error occurred, report that error again. */
   if( pPager->errCode ) return pPager->errCode;
@@ -5118,13 +5334,16 @@ int sqlite3PagerCommitPhaseOne(
   PAGERTRACE(("DATABASE SYNC: File=%s zMaster=%s nSize=%d\n", 
       pPager->zFilename, zMaster, pPager->dbSize));
 
-  if( MEMDB && pPager->dbModified ){
+  /* If no database changes have been made, return early. */
+  if( pPager->eState<PAGER_WRITER_CACHEMOD ) return SQLITE_OK;
+
+  if( MEMDB ){
     /* If this is an in-memory db, or no pages have been written to, or this
     ** function has already been called, it is mostly a no-op.  However, any
     ** backup in progress needs to be restarted.
     */
     sqlite3BackupRestart(pPager->pBackup);
-  }else if( pPager->state!=PAGER_SYNCED && pPager->dbModified ){
+  }else{
     if( pagerUseWal(pPager) ){
       PgHdr *pList = sqlite3PcacheDirtyList(pPager->pPCache);
       if( pList ){
@@ -5203,7 +5422,7 @@ int sqlite3PagerCommitPhaseOne(
       */
   #ifndef SQLITE_OMIT_AUTOVACUUM
       if( pPager->dbSize<pPager->dbOrigSize 
-       && ALWAYS(pPager->journalMode!=PAGER_JOURNALMODE_OFF)
+       && pPager->journalMode!=PAGER_JOURNALMODE_OFF
       ){
         Pgno i;                                   /* Iterator variable */
         const Pgno iSkip = PAGER_MJ_PGNO(pPager); /* Pending lock page */
@@ -5256,7 +5475,7 @@ int sqlite3PagerCommitPhaseOne(
       */
       if( pPager->dbSize!=pPager->dbFileSize ){
         Pgno nNew = pPager->dbSize - (pPager->dbSize==PAGER_MJ_PGNO(pPager));
-        assert( pPager->state>=PAGER_EXCLUSIVE );
+        assert( pPager->eState==PAGER_WRITER_DBMOD );
         rc = pager_truncate(pPager, nNew);
         if( rc!=SQLITE_OK ) goto commit_phase_one_exit;
       }
@@ -5267,11 +5486,12 @@ int sqlite3PagerCommitPhaseOne(
       }
       IOTRACE(("DBSYNC %p\n", pPager))
     }
-
-    pPager->state = PAGER_SYNCED;
   }
 
 commit_phase_one_exit:
+  if( rc==SQLITE_OK && !pagerUseWal(pPager) ){
+    pPager->eState = PAGER_WRITER_FINISHED;
+  }
   return rc;
 }
 
@@ -5304,7 +5524,15 @@ int sqlite3PagerCommitPhaseTwo(Pager *pPager){
   ** fails - make it so that we never reach this point if we do not hold
   ** all necessary locks.
   */
+  assert( pPager->eState==PAGER_WRITER_INITIAL
+       || pPager->eState==PAGER_WRITER_FINISHED
+       || (pagerUseWal(pPager) && pPager->eState==PAGER_WRITER_CACHEMOD)
+  );
+  assert( assert_pager_state(pPager) );
+
+#if 0
   if( NEVER(pPager->state<PAGER_RESERVED) ) return SQLITE_ERROR;
+#endif
 
   /* An optimization. If the database was not actually modified during
   ** this transaction, the pager is running in exclusive-mode and is
@@ -5317,15 +5545,16 @@ int sqlite3PagerCommitPhaseTwo(Pager *pPager){
   ** header. Since the pager is in exclusive mode, there is no need
   ** to drop any locks either.
   */
-  if( pPager->dbModified==0 && pPager->exclusiveMode 
+  if( pPager->eState==PAGER_WRITER_INITIAL 
+   && pPager->exclusiveMode 
    && pPager->journalMode==PAGER_JOURNALMODE_PERSIST
   ){
     assert( pPager->journalOff==JOURNAL_HDR_SZ(pPager) || !pPager->journalOff );
+    pPager->eState = PAGER_READER;
     return SQLITE_OK;
   }
 
   PAGERTRACE(("COMMIT %d\n", PAGERID(pPager)));
-  assert( pPager->state==PAGER_SYNCED || MEMDB || !pPager->dbModified );
   rc = pager_end_transaction(pPager, pPager->setMaster);
   return pager_error(pPager, rc);
 }
@@ -5376,6 +5605,11 @@ int sqlite3PagerCommitPhaseTwo(Pager *pPager){
 int sqlite3PagerRollback(Pager *pPager){
   int rc = SQLITE_OK;                  /* Return code */
   PAGERTRACE(("ROLLBACK %d\n", PAGERID(pPager)));
+
+  /* PagerRollback() is a no-op if called in READER or NONE state. */
+  assert( assert_pager_state(pPager) );
+  if( pPager->eState<=PAGER_READER ) return SQLITE_OK;
+
   if( pagerUseWal(pPager) ){
     int rc2;
 
@@ -5383,15 +5617,15 @@ int sqlite3PagerRollback(Pager *pPager){
     rc2 = pager_end_transaction(pPager, pPager->setMaster);
     if( rc==SQLITE_OK ) rc = rc2;
     rc = pager_error(pPager, rc);
-  }else if( !pPager->dbModified || !isOpen(pPager->jfd) ){
+  }else if( pPager->eState==PAGER_WRITER_INITIAL ){
     rc = pager_end_transaction(pPager, pPager->setMaster);
   }else if( pPager->errCode && pPager->errCode!=SQLITE_FULL ){
-    if( pPager->state>=PAGER_EXCLUSIVE ){
+    if( pPager->eState>=PAGER_WRITER_DBMOD ){
       pager_playback(pPager, 0);
     }
     rc = pPager->errCode;
   }else{
-    if( pPager->state==PAGER_RESERVED ){
+    if( pPager->eState==PAGER_WRITER_CACHEMOD ){
       int rc2;
       rc = pager_playback(pPager, 0);
       rc2 = pager_end_transaction(pPager, pPager->setMaster);
@@ -5459,7 +5693,7 @@ int *sqlite3PagerStats(Pager *pPager){
   a[1] = sqlite3PcachePagecount(pPager->pPCache);
   a[2] = sqlite3PcacheGetCachesize(pPager->pPCache);
   a[3] = pPager->dbSizeValid ? (int) pPager->dbSize : -1;
-  a[4] = pPager->state;
+  a[4] = pPager->eState;
   a[5] = pPager->errCode;
   a[6] = pPager->nHit;
   a[7] = pPager->nMiss;
@@ -5708,6 +5942,10 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){
   Pgno origPgno;               /* The original page number */
 
   assert( pPg->nRef>0 );
+  assert( pPager->eState==PAGER_WRITER_CACHEMOD
+       || pPager->eState==PAGER_WRITER_DBMOD
+  );
+  assert( assert_pager_state(pPager) );
 
   /* In order to be able to rollback, an in-memory database must journal
   ** the page we are moving from.
@@ -5782,7 +6020,6 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){
   origPgno = pPg->pgno;
   sqlite3PcacheMove(pPg, pgno);
   sqlite3PcacheMakeDirty(pPg);
-  pPager->dbModified = 1;
 
   if( needSyncPgno ){
     /* If needSyncPgno is non-zero, then the journal file needs to be 
@@ -5808,7 +6045,6 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){
       }
       return rc;
     }
-    assert( pPager->noSync==0 && !MEMDB );
     pPgHdr->flags |= PGHDR_NEED_SYNC;
     sqlite3PcacheMakeDirty(pPgHdr);
     sqlite3PagerUnref(pPgHdr);
@@ -5949,23 +6185,23 @@ int sqlite3PagerSetJournalMode(Pager *pPager, int eMode){
       ** while it is in use by some other client.
       */
       int rc = SQLITE_OK;
-      int state = pPager->state;
-      if( state<PAGER_SHARED ){
+      int state = pPager->eState;
+      if( state==PAGER_NONE ){
         rc = sqlite3PagerSharedLock(pPager);
       }
-      if( pPager->state==PAGER_SHARED ){
+      if( pPager->eState==PAGER_READER ){
         assert( rc==SQLITE_OK );
         rc = sqlite3OsLock(pPager->fd, RESERVED_LOCK);
       }
       if( rc==SQLITE_OK ){
         sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0);
       }
-      if( rc==SQLITE_OK && state==PAGER_SHARED ){
+      if( rc==SQLITE_OK && state==PAGER_READER ){
         sqlite3OsUnlock(pPager->fd, SHARED_LOCK);
-      }else if( state==PAGER_UNLOCK ){
+      }else if( state==PAGER_NONE ){
         pager_unlock(pPager);
       }
-      assert( state==pPager->state );
+      assert( state==pPager->eState );
     }
   }
 
@@ -5986,7 +6222,7 @@ int sqlite3PagerGetJournalMode(Pager *pPager){
 ** is unmodified.
 */
 int sqlite3PagerOkToChangeJournalMode(Pager *pPager){
-  if( pPager->dbModified ) return 0;
+  if( pPager->eState>=PAGER_WRITER_CACHEMOD ) return 0;
   if( NEVER(isOpen(pPager->jfd) && pPager->journalOff>0) ) return 0;
   return 1;
 }
@@ -6064,7 +6300,7 @@ int sqlite3PagerOpenWal(
 ){
   int rc = SQLITE_OK;             /* Return code */
 
-  assert( pPager->state>=PAGER_SHARED );
+  assert( pPager->eState>=PAGER_READER );
   assert( (pisOpen==0 && !pPager->tempFile && !pPager->pWal) || *pisOpen==0 );
 
   if( !pPager->tempFile && !pPager->pWal ){
index 6fc70d99c5c2f246a5a9342f06c7c5d91abbe005..50a2aecfb2b9deb5ddf9de0f57aa3c02d349b760 100644 (file)
@@ -403,7 +403,6 @@ do_test analyze3-3.6.5 {
 } {SQLITE_OK}
 
 do_test analyze3-3.7.1 {
-breakpoint
   set S [sqlite3_prepare_v2 db {
     SELECT * FROM t1 WHERE a IN (
       ?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?33,
index 4f20a6b9fc04d93e3cfcd4ffdcf01b4ec23d0dd0..a4ac9eacd67d2343da5b522ac684e4a5aa599675 100644 (file)
@@ -253,6 +253,11 @@ db2 close
 # from being inspected externally.
 #
 if {$tcl_platform(platform) != "windows"} {
+
+  # Return a list of two booleans (either 0 or 1). The first is true
+  # if the named file exists. The second is true only if the file
+  # exists and the first 28 bytes contain at least one non-zero byte.
+  #
   proc filestate {fname} {
     set exists 0
     set content 0
@@ -263,6 +268,7 @@ if {$tcl_platform(platform) != "windows"} {
     }
     list $exists $content
   }
+
   do_test exclusive-3.0 {
     filestate test.db-journal
   } {0 0}
index c4d7fa7bc510a7f9acc7c8eacdb1e9018ac66b41..0b6f24a3e44c19590adcea2399b7a55911acc62c 100644 (file)
@@ -456,7 +456,7 @@ do_test wal2-6.1.5 {
     SELECT * FROM t1;
     PRAGMA lock_status;
   }
-} {1 2 main exclusive temp closed}
+} {1 2 main shared temp closed}
 do_test wal2-6.1.6 {
   execsql {
     INSERT INTO t1 VALUES(3, 4);
@@ -522,7 +522,7 @@ do_test wal2-6.2.8 {
     SELECT * FROM t1;
     pragma lock_status;
   }
-} {1 2 3 4 main exclusive temp closed}
+} {1 2 3 4 main shared temp closed}
 do_test wal2-6.2.9 {
   execsql {
     INSERT INTO t1 VALUES(5, 6);
@@ -608,6 +608,11 @@ set READMARK1_SET {
 set READMARK1_READ {
   {4 1 lock shared} {4 1 unlock shared}
 }
+set READMARK1_WRITE {
+  {4 1 lock shared} 
+    {0 1 lock exclusive} {0 1 unlock exclusive} 
+  {4 1 unlock shared}
+}
 
 foreach {tn sql res expected_locks} {
   2 {
@@ -664,13 +669,9 @@ foreach {tn sql res expected_locks} {
 
   9 {
     SELECT * FROM t1 ORDER BY x
-  } {Arthur {Julius Henry} Karl Leonard} { }
+  } {Arthur {Julius Henry} Karl Leonard} $READMARK1_READ
 
-  10 {
-    DELETE FROM t1
-  } {} {
-    $READMARK1_READ
-  }
+  10 { DELETE FROM t1 } {} $READMARK1_WRITE
 
   11 {
     SELECT * FROM t1
@@ -942,7 +943,6 @@ do_test wal2-10.2.2 {
   set hdr [set_tvfs_hdr $::filename] 
   lindex $hdr 0 
 } {3007000}
-breakpoint
 do_test wal2-10.2.3 { 
   lset hdr 0 3007001
   wal_fix_walindex_cksum hdr