]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Set the Pager.eState variable to PAGER_ERROR whenever the pager enters the error...
authordan <dan@noemail.net>
Tue, 3 Aug 2010 18:18:41 +0000 (18:18 +0000)
committerdan <dan@noemail.net>
Tue, 3 Aug 2010 18:18:41 +0000 (18:18 +0000)
FossilOrigin-Name: 4d384761d27425b71555f4f3262778277a955835

manifest
manifest.uuid
src/pager.c

index 736464b9c6ffde07603b53177f201c26cd4d6f74..056d09ed0241282151503671cd88eb49514b3597 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Add\scomments\sto\sclarify\sthe\spurpose\sof\sthe\spager\s"error\sstate".
-D 2010-08-03T15:57:10
+C Set\sthe\sPager.eState\svariable\sto\sPAGER_ERROR\swhenever\sthe\spager\senters\sthe\serror\sstate.
+D 2010-08-03T18:18:42
 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 809f4dfbca43fd64ef27855e193d8544a5054668
+F src/pager.c b48eb75d41fd1bce0e9911209785aef4381258ef
 F src/pager.h 80726162dc3942f59ab27b738fb667b9ba0a89d5
 F src/parse.y 12b7ebd61ea54f0e1b1083ff69cc2c8ce9353d58
 F src/pcache.c 1e9aa2dbc0845b52e1b51cc39753b6d1e041cb07
@@ -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 16dcf5a6d3ebff9a734705693b5ac740f4ff2cc1
-R f5f08ae2bf16feac4ddffb2e7526923b
+P fde4c59782e98366792e1552fd122e7d80a277ff
+R dd009cdb2bebe6c9ea312f79af5244c4
 U dan
-Z 158c2b787243a4910abe77703b2eb315
+Z 18695678602885240568b79d8561ccf5
index c2bab4d837eecafa6cb5e77cba043b487ffa25f7..9923f3bbac47f22252ae9d1555756612d0c29216 100644 (file)
@@ -1 +1 @@
-fde4c59782e98366792e1552fd122e7d80a277ff
\ No newline at end of file
+4d384761d27425b71555f4f3262778277a955835
\ No newline at end of file
index 1129021e911ace40d0d4ca7286d112a05301caae..e3f73d953f33eca9229698541f9926826dfac577 100644 (file)
@@ -291,6 +291,7 @@ int sqlite3PagerTrace=1;  /* True to enable tracing */
 #define PAGER_WRITER_CACHEMOD       3
 #define PAGER_WRITER_DBMOD          4
 #define PAGER_WRITER_FINISHED       5
+#define PAGER_ERROR                 6
 
 
 /*
@@ -696,6 +697,7 @@ static int assert_pager_state(Pager *p){
        || p->eState==PAGER_WRITER_CACHEMOD
        || p->eState==PAGER_WRITER_DBMOD
        || p->eState==PAGER_WRITER_FINISHED
+       || p->eState==PAGER_ERROR
   );
 
   /* Regardless of the current state, a temp-file connection always behaves
@@ -718,21 +720,27 @@ static int assert_pager_state(Pager *p){
     case PAGER_NONE:
       assert( !MEMDB );
       assert( !p->tempFile );
+      assert( pPager->errCode==SQLITE_OK );
+      assert( sqlite3PcacheRefCount(pPager->pPCache)==0 );
       break;
 
     case PAGER_READER:
+      assert( pPager->errCode==SQLITE_OK );
       assert( p->eLock>=SHARED_LOCK || p->noReadlock );
       break;
 
     case PAGER_WRITER_INITIAL:
+      assert( pPager->errCode==SQLITE_OK );
       if( !pagerUseWal(pPager) ){
         assert( p->eLock>=RESERVED_LOCK );
       }
       assert( pPager->dbSize==pPager->dbOrigSize );
       assert( pPager->dbOrigSize==pPager->dbFileSize );
+      assert( pPager->setMaster==0 );
       break;
 
     case PAGER_WRITER_CACHEMOD:
+      assert( pPager->errCode==SQLITE_OK );
       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
@@ -749,6 +757,7 @@ static int assert_pager_state(Pager *p){
       break;
 
     case PAGER_WRITER_DBMOD:
+      assert( pPager->errCode==SQLITE_OK );
       assert( !pagerUseWal(pPager) );
       assert( p->eLock>=EXCLUSIVE_LOCK || pagerUseWal(pPager) );
       assert( isOpen(p->jfd) 
@@ -758,6 +767,7 @@ static int assert_pager_state(Pager *p){
       break;
 
     case PAGER_WRITER_FINISHED:
+      assert( pPager->errCode==SQLITE_OK );
       assert( !pagerUseWal(pPager) );
       assert( p->eLock>=EXCLUSIVE_LOCK || pagerUseWal(pPager) );
       assert( isOpen(p->jfd) 
@@ -765,6 +775,15 @@ static int assert_pager_state(Pager *p){
            || p->journalMode==PAGER_JOURNALMODE_WAL 
       );
       break;
+
+    case PAGER_ERROR:
+      /* There must be at least one outstanding reference to the pager if
+      ** in ERROR state. Otherwise the pager should have already dropped
+      ** back to NONE state.
+      */
+      assert( pPager->errCode!=SQLITE_OK );
+      assert( sqlite3PcacheRefCount(pPager->pPCache)>0 );
+      break;
   }
 
   return 1;
@@ -787,7 +806,8 @@ static char *print_pager_state(Pager *p){
         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->eState==PAGER_WRITER_FINISHED ? "WRITER_FINISHED" :
+        p->eState==PAGER_ERROR           ? "ERROR" : "?error?"
       , p->eLock==NO_LOCK         ? "NONE" :
         p->eLock==RESERVED_LOCK   ? "RESERVED" :
         p->eLock==EXCLUSIVE_LOCK  ? "EXCLUSIVE" :
@@ -1443,16 +1463,11 @@ static PgHdr *pager_lookup(Pager *pPager, Pgno pgno){
 }
 
 /*
-** Unless the pager is in error-state, discard all in-memory pages. If
-** the pager is in error-state, then this call is a no-op.
-**
-** TODO: Why can we not reset the pager while in error state?
+** Discard the entire contents of the in-memory page-cache.
 */
 static void pager_reset(Pager *pPager){
-  if( SQLITE_OK==pPager->errCode ){
-    sqlite3BackupRestart(pPager->pBackup);
-    sqlite3PcacheClear(pPager->pPCache);
-  }
+  sqlite3BackupRestart(pPager->pBackup);
+  sqlite3PcacheClear(pPager->pPCache);
 }
 
 /*
@@ -1505,8 +1520,16 @@ static int addToSavepointBitvecs(Pager *pPager, Pgno pgno){
 ** treated as a hot-journal and rolled back.
 */
 static void pager_unlock(Pager *pPager){
-  if( !pPager->exclusiveMode ){
-    int rc = SQLITE_OK;          /* Return code */
+
+  sqlite3BitvecDestroy(pPager->pInJournal);
+  pPager->pInJournal = 0;
+  releaseAllSavepoints(pPager);
+
+  if( pagerUseWal(pPager) ){
+    assert( !isOpen(pPager->jfd) );
+    sqlite3WalEndReadTransaction(pPager->pWal);
+    pPager->eState = PAGER_NONE;
+  }else if( !pPager->exclusiveMode ){
     int iDc = isOpen(pPager->fd)?sqlite3OsDeviceCharacteristics(pPager->fd):0;
 
     /* If the operating system support deletion of open files, then
@@ -1520,40 +1543,33 @@ 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)
     ){
       sqlite3OsClose(pPager->jfd);
     }
+    osUnlock(pPager, NO_LOCK);
 
-    sqlite3BitvecDestroy(pPager->pInJournal);
-    pPager->pInJournal = 0;
-    releaseAllSavepoints(pPager);
-
-    if( pagerUseWal(pPager) ){
-      sqlite3WalEndReadTransaction(pPager->pWal);
-    }else{
-      rc = osUnlock(pPager, NO_LOCK);
-    }
-    if( rc ){
-      pPager->errCode = rc;
-    }
-    IOTRACE(("UNLOCK %p\n", pPager))
-
-    /* If Pager.errCode is set, the contents of the pager cache cannot be
-    ** trusted. Now that the pager file is unlocked, the contents of the
-    ** cache can be discarded and the error code safely cleared.
+    /* The pager state may be changed from PAGER_ERROR to PAGER_NONE here
+    ** without clearing the error code. This is intentional - the error
+    ** code is cleared and the cache reset in the block below.
     */
-    if( pPager->errCode ){
-      if( rc==SQLITE_OK ){
-        pPager->errCode = SQLITE_OK;
-      }
-      pager_reset(pPager);
-    }
+    assert( pPager->errCode || pPager->eState!=PAGER_ERROR );
+    pPager->changeCountDone = 0;
+    pPager->eState = PAGER_NONE;
+    IOTRACE(("UNLOCK %p\n", pPager))
+  }
 
+  /* If Pager.errCode is set, the contents of the pager cache cannot be
+  ** trusted. Now that there are no outstanding references to the pager,
+  ** it can safely move back to PAGER_NONE state. This happens in both
+  ** normal and exclusive-locking mode.
+  */
+  if( pPager->errCode ){
+    pager_reset(pPager);
     pPager->changeCountDone = 0;
     pPager->eState = PAGER_NONE;
+    pPager->errCode = SQLITE_OK;
   }
 }
 
@@ -1586,6 +1602,7 @@ static int pager_error(Pager *pPager, int rc){
   );
   if( rc2==SQLITE_FULL || rc2==SQLITE_IOERR ){
     pPager->errCode = rc;
+    pPager->eState = PAGER_ERROR;
   }
   return rc;
 }
@@ -1652,6 +1669,7 @@ static int pager_end_transaction(Pager *pPager, int hasMaster){
 
   /* Do nothing if the pager does not have an open write transaction. */
   assert( assert_pager_state(pPager) );
+  assert( pPager->eState!=PAGER_ERROR );
   if( pPager->eState<PAGER_WRITER_INITIAL && pPager->eLock<RESERVED_LOCK ){
     return SQLITE_OK;
   }
@@ -1741,7 +1759,8 @@ static int pager_end_transaction(Pager *pPager, int hasMaster){
 ** call to pager_unlock(), as described above.
 */
 static void pagerUnlockAndRollback(Pager *pPager){
-  if( pPager->errCode==SQLITE_OK ){
+  if( pPager->eState!=PAGER_ERROR ){
+    assert( assert_pager_state(pPager) );
     if( pPager->eState>=PAGER_WRITER_INITIAL ){
       sqlite3BeginBenignMalloc();
       sqlite3PagerRollback(pPager);
@@ -2199,6 +2218,7 @@ delmaster_out:
 */
 static int pager_truncate(Pager *pPager, Pgno nPage){
   int rc = SQLITE_OK;
+  assert( pPager->eState!=PAGER_ERROR );
   if( pPager->eState>=PAGER_WRITER_DBMOD && isOpen(pPager->fd) ){
     i64 currentSize, newSize;
     /* TODO: Is it safe to use Pager.dbFileSize here? */
@@ -2833,6 +2853,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->eState!=PAGER_ERROR );
   assert( pPager->eState>=PAGER_WRITER_INITIAL );
 
   /* Allocate a bitvec to use to store the set of pages rolled back */
@@ -3080,6 +3101,12 @@ void sqlite3PagerSetBusyhandler(
 int sqlite3PagerSetPagesize(Pager *pPager, u16 *pPageSize, int nReserve){
   int rc = pPager->errCode;
 
+  /* It is not possible to do a full assert_pager_state() here, as this
+  ** function may be called from within PagerOpen(), before the state
+  ** of the Pager object is internally consistent.
+  */
+  assert( rc==SQLITE_OK || pPager->eState==PAGER_ERROR );
+
   if( rc==SQLITE_OK ){
     u16 pageSize = *pPageSize;
     assert( pageSize==0 || (pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE) );
@@ -3370,7 +3397,7 @@ int sqlite3PagerClose(Pager *pPager){
 
   disable_simulated_io_errors();
   sqlite3BeginBenignMalloc();
-  pPager->errCode = 0;
+  /* pPager->errCode = 0; */
   pPager->exclusiveMode = 0;
 #ifndef SQLITE_OMIT_WAL
   sqlite3WalClose(pPager->pWal,
@@ -3383,14 +3410,19 @@ int sqlite3PagerClose(Pager *pPager){
   if( MEMDB ){
     pager_unlock(pPager);
   }else{
-    /* Set Pager.journalHdr to -1 for the benefit of the pager_playback() 
-    ** call which may be made from within pagerUnlockAndRollback(). If it
-    ** is not -1, then the unsynced portion of an open journal file may
-    ** be played back into the database. If a power failure occurs while
-    ** this is happening, the database may become corrupt.
+    /* If it is open, sync the journal file before calling UnlockAndRollback.
+    ** If this is not done, then an unsynced portion of the open journal 
+    ** file may be played back into the database. If a power failure occurs 
+    ** while this is happening, the database could become corrupt.
+    **
+    ** If an error occurs while trying to sync the journal, shift the pager
+    ** into the ERROR state. This causes UnlockAndRollback to unlock the
+    ** database and close the journal file without attempting to roll it
+    ** back or finalize it. The next database user will have to do hot-journal
+    ** rollback before accessing the database file.
     */
     if( isOpen(pPager->jfd) ){
-      pPager->errCode = pagerSyncHotJournal(pPager);
+      pager_error(pPager, pagerSyncHotJournal(pPager));
     }
     pagerUnlockAndRollback(pPager);
   }
@@ -4359,24 +4391,10 @@ int sqlite3PagerSharedLock(Pager *pPager){
   assert( assert_pager_state(pPager) );
   if( NEVER(MEMDB && pPager->errCode) ){ return pPager->errCode; }
 
-  /* If this database is in an error-state, now is a chance to clear
-  ** the error. Discard the contents of the pager-cache and rollback
-  ** any hot journal in the file-system.
-  */
-  if( pPager->errCode ){
-    if( isOpen(pPager->jfd) || pPager->zJournal ){
-      isErrorReset = 1;
-    }
-    pPager->errCode = SQLITE_OK;
-    pPager->eState = PAGER_NONE;
-    pager_reset(pPager);
-  }
-
   if( !pagerUseWal(pPager) && pPager->eState==PAGER_NONE ){
     sqlite3_vfs * const pVfs = pPager->pVfs;
 
     assert( !MEMDB && !pPager->tempFile );
-    assert( sqlite3PcacheRefCount(pPager->pPCache)==0 );
     assert( pPager->noReadlock==0 || pPager->readOnly );
 
     if( pPager->noReadlock==0 ){
@@ -4474,6 +4492,7 @@ int sqlite3PagerSharedLock(Pager *pPager){
       if( isOpen(pPager->jfd) ){
         rc = pagerSyncHotJournal(pPager);
         if( rc==SQLITE_OK ){
+          assert( pPager->eState==PAGER_NONE );
           pPager->eState = PAGER_WRITER_FINISHED;
           rc = pager_playback(pPager, 1);
           pPager->eState = PAGER_NONE;
@@ -4540,16 +4559,13 @@ int sqlite3PagerSharedLock(Pager *pPager){
   }
 
   if( pPager->eState==PAGER_NONE && rc==SQLITE_OK ){
-    pPager->eState = PAGER_NONE;
     rc = pagerPagecount(pPager, &pPager->dbSize);
   }
 
  failed:
   if( rc!=SQLITE_OK ){
     pager_unlock(pPager);
-    assert( (pPager->eState==PAGER_NONE)
-        ||  (pPager->exclusiveMode && pagerUseWal(pPager))
-    );
+    assert( pPager->eState==PAGER_NONE );
   }else{
     pPager->eState = PAGER_READER;
   }
@@ -4962,14 +4978,16 @@ static int pager_write(PgHdr *pPg){
 
   /* 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.
+  ** It is never called in the ERROR state.
   */
-  assert( pPager->eState>=PAGER_WRITER_INITIAL );
-  assert( pPager->eState!=PAGER_WRITER_FINISHED );
+  assert( pPager->eState==PAGER_WRITER_INITIAL
+       || pPager->eState==PAGER_WRITER_CACHEMOD
+       || pPager->eState==PAGER_WRITER_DBMOD
+  );
   assert( assert_pager_state(pPager) );
 
   /* If an error has been previously detected, report the same error
-  ** again.
-  */
+  ** again. This should not happen, but the check provides robustness. */
   if( NEVER(pPager->errCode) )  return pPager->errCode;
 
   /* Higher-level routines never call this function if database is not
@@ -5113,6 +5131,7 @@ int sqlite3PagerWrite(DbPage *pDbPage){
   Pgno nPagePerSector = (pPager->sectorSize/pPager->pageSize);
 
   assert( pPager->eState>=PAGER_WRITER_INITIAL );
+  assert( pPager->eState!=PAGER_ERROR );
   assert( assert_pager_state(pPager) );
 
   if( nPagePerSector>1 ){
@@ -5408,11 +5427,6 @@ int sqlite3PagerCommitPhaseOne(
   );
   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;
 
@@ -5682,8 +5696,12 @@ 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. */
+  /* PagerRollback() is a no-op if called in READER or NONE state. If
+  ** the pager is already in the ERROR state, the rollback is not 
+  ** attempted here. Instead, the error code is returned to the caller.
+  */
   assert( assert_pager_state(pPager) );
+  if( pPager->eState==PAGER_ERROR ) return pPager->errCode;
   if( pPager->eState<=PAGER_READER ) return SQLITE_OK;
 
   if( pagerUseWal(pPager) ){
@@ -5691,25 +5709,13 @@ int sqlite3PagerRollback(Pager *pPager){
     rc = sqlite3PagerSavepoint(pPager, SAVEPOINT_ROLLBACK, -1);
     rc2 = pager_end_transaction(pPager, pPager->setMaster);
     if( rc==SQLITE_OK ) rc = rc2;
-  }else if( pPager->eState==PAGER_WRITER_INITIAL ){
+  }else if( !isOpen(pPager->jfd) ){
     rc = pager_end_transaction(pPager, 0);
-  }else if( pPager->errCode && pPager->errCode!=SQLITE_FULL ){
-    if( pPager->eState>=PAGER_WRITER_DBMOD ){
-      pager_playback(pPager, 0);
-    }
-    rc = pPager->errCode;
   }else{
-    if( pPager->eState==PAGER_WRITER_CACHEMOD ){
-      int rc2;
-      rc = pager_playback(pPager, 0);
-      rc2 = pager_end_transaction(pPager, pPager->setMaster);
-      if( rc==SQLITE_OK ){
-        rc = rc2;
-      }
-    }else{
-      rc = pager_playback(pPager, 0);
-    }
+    rc = pager_playback(pPager, 0);
   }
+  assert( pPager->eState==PAGER_READER || rc!=SQLITE_OK );
+  assert( rc==SQLITE_OK || rc==SQLITE_FULL || (rc&0xFF)==SQLITE_IOERR );
 
   /* If an error occurs during a ROLLBACK, we can no longer trust the pager
   ** cache. So call pager_error() on the way out to make any error persistent.