#define PAGER_WRITER_CACHEMOD 3
#define PAGER_WRITER_DBMOD 4
#define PAGER_WRITER_FINISHED 5
+#define PAGER_ERROR 6
/*
|| 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
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
break;
case PAGER_WRITER_DBMOD:
+ assert( pPager->errCode==SQLITE_OK );
assert( !pagerUseWal(pPager) );
assert( p->eLock>=EXCLUSIVE_LOCK || pagerUseWal(pPager) );
assert( isOpen(p->jfd)
break;
case PAGER_WRITER_FINISHED:
+ assert( pPager->errCode==SQLITE_OK );
assert( !pagerUseWal(pPager) );
assert( p->eLock>=EXCLUSIVE_LOCK || pagerUseWal(pPager) );
assert( isOpen(p->jfd)
|| 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;
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" :
}
/*
-** 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);
}
/*
** 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
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;
}
}
);
if( rc2==SQLITE_FULL || rc2==SQLITE_IOERR ){
pPager->errCode = rc;
+ pPager->eState = PAGER_ERROR;
}
return rc;
}
/* 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;
}
** 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);
*/
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? */
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 */
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) );
disable_simulated_io_errors();
sqlite3BeginBenignMalloc();
- pPager->errCode = 0;
+ /* pPager->errCode = 0; */
pPager->exclusiveMode = 0;
#ifndef SQLITE_OMIT_WAL
sqlite3WalClose(pPager->pWal,
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);
}
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 ){
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;
}
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;
}
/* 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
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 ){
);
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;
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) ){
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.