1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-08-08 14:02:16 +03:00

Fix some problems with error recovery introduced while reworking pager state.

FossilOrigin-Name: 77eaab6f77c53cc4f429b65dfcf287ad6084c2da
This commit is contained in:
dan
2010-08-04 19:14:22 +00:00
parent d5e0101be0
commit 431b0b4291
3 changed files with 96 additions and 97 deletions

View File

@@ -127,7 +127,25 @@ int sqlite3PagerTrace=1; /* True to enable tracing */
/*
** The Pager.eState variable stores the current 'state' of a pager. A
** pager may be in any one of the following six states:
** pager may be in any one of the seven states shown in the following
** state diagram.
**
** NONE <------+------+
** | | |
** V | |
** +---------> READER-------+ |
** | | |
** | V |
** |<-------WRITER_INITIAL-----> ERROR
** | | ^
** | V |
** |<------WRITER_CACHEMOD-------->|
** | | |
** | V |
** |<-------WRITER_DBMOD---------->|
** | | |
** | V |
** +<------WRITER_FINISHED-------->+
**
** NONE:
**
@@ -237,26 +255,6 @@ int sqlite3PagerTrace=1; /* True to enable tracing */
** state.
**
**
** State diagram:
**
** NONE <------+------+
** | | |
** V | |
** +---------> READER-------+ |
** | | |
** | V |
** |<-------WRITER_INITIAL-----> ERROR
** | | ^
** | V |
** |<------WRITER_CACHEMOD-------->|
** | | |
** | V |
** |<-------WRITER_DBMOD---------->|
** | | |
** | V |
** +<------WRITER_FINISHED-------->+
**
**
** State transitions and the [function] that performs each:
**
** NONE -> READER [PagerSharedLock]
@@ -265,12 +263,11 @@ int sqlite3PagerTrace=1; /* True to enable tracing */
** 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]
** WRITER_*** -> READER [pager_end_transaction]
** WRITER_*** -> ERROR [pager_error]
**
** READER -> NONE [pager_unlock]
** ERROR -> NONE [pager_unlock]
**
** Notes:
**
@@ -716,6 +713,11 @@ static int assert_pager_state(Pager *p){
/* Check that MEMDB implies noSync. */
assert( !MEMDB || p->noSync );
/* If changeCountDone is set, a RESERVED lock or greater must be held
** on the file.
*/
assert( pPager->changeCountDone==0 || pPager->eLock>=RESERVED_LOCK );
switch( p->eState ){
case PAGER_NONE:
assert( !MEMDB );
@@ -891,13 +893,31 @@ static int write32bits(sqlite3_file *fd, i64 offset, u32 val){
** If file pFd is open, call sqlite3OsUnlock() on it.
*/
static int osUnlock(Pager *pPager, int eLock){
if( !isOpen(pPager->fd) ){
return SQLITE_OK;
int rc = SQLITE_OK;
if( isOpen(pPager->fd) ){
assert( pPager->eLock>=eLock );
assert( eLock!=NO_LOCK || pagerUseWal(pPager)==0 );
rc = sqlite3OsUnlock(pPager->fd, eLock);
if( rc==SQLITE_OK ){
pPager->eLock = eLock;
}
}
assert( pPager->eLock>=eLock );
assert( eLock!=NO_LOCK || pagerUseWal(pPager)==0 );
pPager->eLock = eLock;
return sqlite3OsUnlock(pPager->fd, eLock);
return rc;
}
static int osLock(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;
IOTRACE(("LOCK %p %d\n", pPager, locktype))
}
}
return rc;
}
/*
@@ -1565,7 +1585,7 @@ static void pager_unlock(Pager *pPager){
** it can safely move back to PAGER_NONE state. This happens in both
** normal and exclusive-locking mode.
*/
if( pPager->errCode ){
if( pPager->errCode && !pPager->tempFile ){
pager_reset(pPager);
pPager->changeCountDone = 0;
pPager->eState = PAGER_NONE;
@@ -1728,10 +1748,10 @@ static int pager_end_transaction(Pager *pPager, int hasMaster){
*/
rc2 = sqlite3WalEndWriteTransaction(pPager->pWal);
assert( rc2==SQLITE_OK );
if( !pPager->exclusiveMode && sqlite3WalExclusiveMode(pPager->pWal, 0) ){
rc2 = osUnlock(pPager, SHARED_LOCK);
}
}else if( !pPager->exclusiveMode ){
}
if( !pPager->exclusiveMode
&& (!pagerUseWal(pPager) || sqlite3WalExclusiveMode(pPager->pWal, 0))
){
rc2 = osUnlock(pPager, SHARED_LOCK);
pPager->changeCountDone = 0;
}
@@ -3245,20 +3265,6 @@ 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
@@ -3295,12 +3301,8 @@ static int pager_wait_on_lock(Pager *pPager, int locktype){
rc = SQLITE_OK;
}else{
do {
rc = sqlite3OsLock(pPager->fd, locktype);
rc = osLock(pPager, locktype);
}while( rc==SQLITE_BUSY && pPager->xBusyHandler(pPager->pBusyHandlerArg) );
if( rc==SQLITE_OK ){
pPager->eLock = (u8)locktype;
IOTRACE(("LOCK %p %d\n", pPager, locktype))
}
}
return rc;
}
@@ -4274,7 +4276,7 @@ static int hasHotJournal(Pager *pPager, int *pExists){
rc = sqlite3OsAccess(pVfs, pPager->zJournal, SQLITE_ACCESS_EXISTS, &exists);
}
if( rc==SQLITE_OK && exists ){
int locked; /* True if some process holds a RESERVED lock */
int locked = 0; /* True if some process holds a RESERVED lock */
/* Race condition here: Another process might have been holding the
** the RESERVED lock and have a journal open at the sqlite3OsAccess()
@@ -4298,9 +4300,9 @@ static int hasHotJournal(Pager *pPager, int *pExists){
if( rc==SQLITE_OK ){
if( nPage==0 ){
sqlite3BeginBenignMalloc();
if( sqlite3OsLock(pPager->fd, RESERVED_LOCK)==SQLITE_OK ){
if( osLock(pPager, RESERVED_LOCK)==SQLITE_OK ){
sqlite3OsDelete(pVfs, pPager->zJournal, 0);
sqlite3OsUnlock(pPager->fd, SHARED_LOCK);
osUnlock(pPager, SHARED_LOCK);
}
sqlite3EndBenignMalloc();
}else{
@@ -4379,7 +4381,6 @@ static int hasHotJournal(Pager *pPager, int *pExists){
*/
int sqlite3PagerSharedLock(Pager *pPager){
int rc = SQLITE_OK; /* Return code */
int isErrorReset = 0; /* True if recovering from error state */
/* This routine is only called from b-tree and only when there are no
** outstanding pages. This implies that the pager state should either
@@ -4392,7 +4393,7 @@ int sqlite3PagerSharedLock(Pager *pPager){
if( NEVER(MEMDB && pPager->errCode) ){ return pPager->errCode; }
if( !pagerUseWal(pPager) && pPager->eState==PAGER_NONE ){
sqlite3_vfs * const pVfs = pPager->pVfs;
int bHotJournal = 1; /* True if there exists a hot journal-file */
assert( !MEMDB && !pPager->tempFile );
assert( pPager->noReadlock==0 || pPager->readOnly );
@@ -4408,13 +4409,13 @@ int sqlite3PagerSharedLock(Pager *pPager){
/* 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 ){
rc = hasHotJournal(pPager, &isErrorReset);
if( rc!=SQLITE_OK ){
goto failed;
}
if( pPager->eLock<=SHARED_LOCK ){
rc = hasHotJournal(pPager, &bHotJournal);
}
if( isErrorReset ){
if( rc!=SQLITE_OK ){
goto failed;
}
if( bHotJournal ){
/* Get an EXCLUSIVE lock on the database file. At this point it is
** important that a RESERVED lock is not obtained on the way to the
** EXCLUSIVE lock. If it were, another process might open the
@@ -4430,7 +4431,7 @@ int sqlite3PagerSharedLock(Pager *pPager){
** Unless the pager is in locking_mode=exclusive mode, the lock is
** downgraded to SHARED_LOCK before this function returns.
*/
rc = pagerLock(pPager, EXCLUSIVE_LOCK);
rc = osLock(pPager, EXCLUSIVE_LOCK);
if( rc!=SQLITE_OK ){
goto failed;
}
@@ -4449,19 +4450,19 @@ int sqlite3PagerSharedLock(Pager *pPager){
** function was called and the journal file does not exist.
*/
if( !isOpen(pPager->jfd) ){
int res;
rc = sqlite3OsAccess(pVfs,pPager->zJournal,SQLITE_ACCESS_EXISTS,&res);
if( rc==SQLITE_OK ){
if( res ){
int fout = 0;
int f = SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_JOURNAL;
assert( !pPager->tempFile );
rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, f, &fout);
assert( rc!=SQLITE_OK || isOpen(pPager->jfd) );
if( rc==SQLITE_OK && fout&SQLITE_OPEN_READONLY ){
rc = SQLITE_CANTOPEN_BKPT;
sqlite3OsClose(pPager->jfd);
}
sqlite3_vfs * const pVfs = pPager->pVfs;
int bExists; /* True if journal file exists */
rc = sqlite3OsAccess(
pVfs, pPager->zJournal, SQLITE_ACCESS_EXISTS, &bExists);
if( rc==SQLITE_OK && bExists ){
int fout = 0;
int f = SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_JOURNAL;
assert( !pPager->tempFile );
rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, f, &fout);
assert( rc!=SQLITE_OK || isOpen(pPager->jfd) );
if( rc==SQLITE_OK && fout&SQLITE_OPEN_READONLY ){
rc = SQLITE_CANTOPEN_BKPT;
sqlite3OsClose(pPager->jfd);
}
}
}
@@ -4581,9 +4582,7 @@ int sqlite3PagerSharedLock(Pager *pPager){
** nothing to rollback, so this routine is a no-op.
*/
static void pagerUnlockIfUnused(Pager *pPager){
if( (sqlite3PcacheRefCount(pPager->pPCache)==0)
&& (!pPager->exclusiveMode || pPager->journalOff>0)
){
if( (sqlite3PcacheRefCount(pPager->pPCache)==0) ){
pagerUnlockAndRollback(pPager);
}
}
@@ -4907,7 +4906,7 @@ int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory){
** exclusive lock on the database is not already held, obtain it now.
*/
if( pPager->exclusiveMode && sqlite3WalExclusiveMode(pPager->pWal, -1) ){
rc = pagerLock(pPager, EXCLUSIVE_LOCK);
rc = osLock(pPager, EXCLUSIVE_LOCK);
if( rc!=SQLITE_OK ){
return rc;
}
@@ -4926,7 +4925,7 @@ int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory){
** busy-handler callback can be used when upgrading to the EXCLUSIVE
** lock, but not when obtaining the RESERVED lock.
*/
rc = pagerLock(pPager, RESERVED_LOCK);
rc = osLock(pPager, RESERVED_LOCK);
if( rc==SQLITE_OK && exFlag ){
rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
}
@@ -6263,13 +6262,13 @@ int sqlite3PagerSetJournalMode(Pager *pPager, int eMode){
}
if( pPager->eState==PAGER_READER ){
assert( rc==SQLITE_OK );
rc = sqlite3OsLock(pPager->fd, RESERVED_LOCK);
rc = osLock(pPager, RESERVED_LOCK);
}
if( rc==SQLITE_OK ){
sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0);
}
if( rc==SQLITE_OK && state==PAGER_READER ){
sqlite3OsUnlock(pPager->fd, SHARED_LOCK);
osUnlock(pPager, SHARED_LOCK);
}else if( state==PAGER_NONE ){
pager_unlock(pPager);
}
@@ -6417,7 +6416,7 @@ int sqlite3PagerCloseWal(Pager *pPager){
*/
if( !pPager->pWal ){
int logexists = 0;
rc = sqlite3OsLock(pPager->fd, SQLITE_LOCK_SHARED);
rc = osLock(pPager, SHARED_LOCK);
if( rc==SQLITE_OK ){
rc = sqlite3OsAccess(
pPager->pVfs, pPager->zWal, SQLITE_ACCESS_EXISTS, &logexists
@@ -6433,7 +6432,7 @@ int sqlite3PagerCloseWal(Pager *pPager){
** the database file, the log and log-summary files will be deleted.
*/
if( rc==SQLITE_OK && pPager->pWal ){
rc = sqlite3OsLock(pPager->fd, SQLITE_LOCK_EXCLUSIVE);
rc = osLock(pPager, EXCLUSIVE_LOCK);
if( rc==SQLITE_OK ){
rc = sqlite3WalClose(pPager->pWal,
(pPager->noSync ? 0 : pPager->sync_flags),
@@ -6443,7 +6442,7 @@ int sqlite3PagerCloseWal(Pager *pPager){
}else{
/* If we cannot get an EXCLUSIVE lock, downgrade the PENDING lock
** that we did get back to SHARED. */
sqlite3OsUnlock(pPager->fd, SQLITE_LOCK_SHARED);
osUnlock(pPager, SQLITE_LOCK_SHARED);
}
}
return rc;