mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-05 15:55:57 +03:00
Clarify error handling in pager code. No functional changes. (CVS 2956)
FossilOrigin-Name: 7b48836214ea3152f46e2dffb097ae7ea14901f4
This commit is contained in:
142
src/pager.c
142
src/pager.c
@@ -18,7 +18,7 @@
|
||||
** file simultaneously, or one process from reading the database while
|
||||
** another is writing.
|
||||
**
|
||||
** @(#) $Id: pager.c,v 1.237 2006/01/15 20:28:28 drh Exp $
|
||||
** @(#) $Id: pager.c,v 1.238 2006/01/16 11:29:19 danielk1977 Exp $
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_DISKIO
|
||||
#include "sqliteInt.h"
|
||||
@@ -225,6 +225,14 @@ struct PgHistory {
|
||||
|
||||
/*
|
||||
** A open page cache is an instance of the following structure.
|
||||
**
|
||||
** Pager.errCode may be set to SQLITE_IOERR, SQLITE_CORRUPT, SQLITE_PROTOCOL
|
||||
** or SQLITE_FULL. Once one of the first three errors occurs, it persists
|
||||
** and is returned as the result of every major pager API call. The
|
||||
** SQLITE_FULL return code is slightly different. It persists only until the
|
||||
** next successful rollback is performed on the pager cache. Also,
|
||||
** SQLITE_FULL does not affect the sqlite3pager_get() and sqlite3pager_lookup()
|
||||
** APIs, they may still be used successfully.
|
||||
*/
|
||||
struct Pager {
|
||||
u8 journalOpen; /* True if journal file descriptors is valid */
|
||||
@@ -237,7 +245,7 @@ struct Pager {
|
||||
u8 noSync; /* Do not sync the journal if true */
|
||||
u8 fullSync; /* Do extra syncs of the journal for robustness */
|
||||
u8 state; /* PAGER_UNLOCK, _SHARED, _RESERVED, etc. */
|
||||
u8 errMask; /* One of several kinds of errors */
|
||||
u8 errCode; /* One of several kinds of errors */
|
||||
u8 tempFile; /* zFilename is a temporary file */
|
||||
u8 readOnly; /* True for a read-only database */
|
||||
u8 needSync; /* True if an fsync() is needed on the journal */
|
||||
@@ -299,39 +307,6 @@ struct Pager {
|
||||
# define TEST_INCR(x)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** These are bits that can be set in Pager.errMask.
|
||||
**
|
||||
** TODO: Maybe we just want a variable - Pager.errCode. Can we really
|
||||
** have two simultaneous error conditions?
|
||||
**
|
||||
** Recovering from an SQLITE_FULL, SQLITE_LOCK, SQLITE_CORRUPT or
|
||||
** SQLITE_IOERR error is not a simple matter, particularly if the pager
|
||||
** cache is shared between multiple connections.
|
||||
**
|
||||
** SQLITE_FULL (PAGER_ERR_FULL):
|
||||
** Cleared when the transaction is rolled back.
|
||||
**
|
||||
** SQLITE_CORRUPT (PAGER_ERR_CORRUPT):
|
||||
** Cannot be cleared. The upper layer must close the current pager
|
||||
** and open a new one on the same file to continue.
|
||||
**
|
||||
** SQLITE_PROTOCOL (PAGER_ERR_LOCK):
|
||||
** This error only occurs if an internal error occurs or another process
|
||||
** is not following the sqlite locking protocol (i.e. someone is
|
||||
** manipulating the database file using something other than sqlite).
|
||||
** This is handled in the same way as database corruption - the error
|
||||
** cannot be cleared except by closing the current pager and opening
|
||||
** a brand new one on the same file.
|
||||
**
|
||||
** SQLITE_IOERR (PAGER_ERR_DISK):
|
||||
** Cleared when the transaction is rolled back.
|
||||
*/
|
||||
#define PAGER_ERR_FULL 0x01 /* a write() failed */
|
||||
#define PAGER_ERR_LOCK 0x02 /* error in the locking protocol */
|
||||
#define PAGER_ERR_CORRUPT 0x04 /* database or journal corruption */
|
||||
#define PAGER_ERR_DISK 0x08 /* general disk I/O error - bad hard drive? */
|
||||
|
||||
/*
|
||||
** Journal files begin with the following magic string. The data
|
||||
** was obtained from /dev/random. It is used only as a sanity check.
|
||||
@@ -480,37 +455,19 @@ static u32 retrieve32bits(PgHdr *p, int offset){
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Convert the bits in the pPager->errMask into an approprate
|
||||
** return code.
|
||||
*/
|
||||
static int pager_errcode(Pager *pPager){
|
||||
int rc = SQLITE_OK;
|
||||
if( pPager->errMask & PAGER_ERR_LOCK ) rc = SQLITE_PROTOCOL;
|
||||
if( pPager->errMask & PAGER_ERR_DISK ) rc = SQLITE_IOERR;
|
||||
if( pPager->errMask & PAGER_ERR_FULL ) rc = SQLITE_FULL;
|
||||
if( pPager->errMask & PAGER_ERR_CORRUPT ) rc = SQLITE_CORRUPT;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function should be called when an error occurs within the pager
|
||||
** code to set the appropriate bits in Pager.errMask.
|
||||
** code to set Pager.errCode.
|
||||
*/
|
||||
static int pager_error(Pager *pPager, int rc){
|
||||
switch( rc ){
|
||||
case SQLITE_PROTOCOL:
|
||||
pPager->errMask |= PAGER_ERR_LOCK;
|
||||
break;
|
||||
case SQLITE_IOERR:
|
||||
pPager->errMask |= PAGER_ERR_DISK;
|
||||
break;
|
||||
case SQLITE_FULL:
|
||||
pPager->errMask |= PAGER_ERR_FULL;
|
||||
break;
|
||||
case SQLITE_CORRUPT:
|
||||
pPager->errMask |= PAGER_ERR_CORRUPT;
|
||||
break;
|
||||
assert( pPager->errCode==SQLITE_FULL || pPager->errCode==SQLITE_OK );
|
||||
if(
|
||||
rc==SQLITE_FULL ||
|
||||
rc==SQLITE_IOERR ||
|
||||
rc==SQLITE_CORRUPT ||
|
||||
rc==SQLITE_PROTOCOL
|
||||
){
|
||||
pPager->errCode = rc;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -537,7 +494,7 @@ static u32 pager_pagehash(PgHdr *pPage){
|
||||
#define CHECK_PAGE(x) checkPage(x)
|
||||
static void checkPage(PgHdr *pPg){
|
||||
Pager *pPager = pPg->pPager;
|
||||
assert( !pPg->pageHash || pPager->errMask || MEMDB || pPg->dirty ||
|
||||
assert( !pPg->pageHash || pPager->errCode || MEMDB || pPg->dirty ||
|
||||
pPg->pageHash==pager_pagehash(pPg) );
|
||||
}
|
||||
|
||||
@@ -879,7 +836,7 @@ static PgHdr *pager_lookup(Pager *pPager, Pgno pgno){
|
||||
*/
|
||||
static void pager_reset(Pager *pPager){
|
||||
PgHdr *pPg, *pNext;
|
||||
if( pPager->errMask ) return;
|
||||
if( pPager->errCode ) return;
|
||||
for(pPg=pPager->pAll; pPg; pPg=pNext){
|
||||
pNext = pPg->pNextAll;
|
||||
sqliteFree(pPg);
|
||||
@@ -1500,8 +1457,7 @@ static int pager_stmt_playback(Pager *pPager){
|
||||
|
||||
end_stmt_playback:
|
||||
if( rc!=SQLITE_OK ){
|
||||
pPager->errMask |= PAGER_ERR_CORRUPT;
|
||||
rc = SQLITE_CORRUPT;
|
||||
rc = pager_error(pPager, SQLITE_CORRUPT);
|
||||
}else{
|
||||
pPager->journalOff = szJ;
|
||||
/* pager_reload_cache(pPager); */
|
||||
@@ -1833,7 +1789,7 @@ int sqlite3pager_pagecount(Pager *pPager){
|
||||
n = pPager->dbSize;
|
||||
} else {
|
||||
if( sqlite3OsFileSize(pPager->fd, &n)!=SQLITE_OK ){
|
||||
pPager->errMask |= PAGER_ERR_DISK;
|
||||
pager_error(pPager, SQLITE_IOERR);
|
||||
return 0;
|
||||
}
|
||||
if( n>0 && n<pPager->pageSize ){
|
||||
@@ -1979,8 +1935,8 @@ static int pager_wait_on_lock(Pager *pPager, int locktype){
|
||||
int sqlite3pager_truncate(Pager *pPager, Pgno nPage){
|
||||
int rc;
|
||||
sqlite3pager_pagecount(pPager);
|
||||
if( pPager->errMask!=0 ){
|
||||
rc = pager_errcode(pPager);
|
||||
if( pPager->errCode ){
|
||||
rc = pPager->errCode;
|
||||
return rc;
|
||||
}
|
||||
if( nPage>=(unsigned)pPager->dbSize ){
|
||||
@@ -2043,7 +1999,7 @@ int sqlite3pager_close(Pager *pPager){
|
||||
if( !MEMDB ){
|
||||
sqlite3OsUnlock(pPager->fd, NO_LOCK);
|
||||
}
|
||||
assert( pPager->errMask || pPager->journalOpen==0 );
|
||||
assert( pPager->errCode || pPager->journalOpen==0 );
|
||||
break;
|
||||
}
|
||||
case PAGER_SHARED: {
|
||||
@@ -2070,7 +2026,7 @@ int sqlite3pager_close(Pager *pPager){
|
||||
sqliteFree(pPg);
|
||||
}
|
||||
TRACE2("CLOSE %d\n", PAGERID(pPager));
|
||||
assert( pPager->errMask || (pPager->journalOpen==0 && pPager->stmtOpen==0) );
|
||||
assert( pPager->errCode || (pPager->journalOpen==0 && pPager->stmtOpen==0) );
|
||||
if( pPager->journalOpen ){
|
||||
sqlite3OsClose(&pPager->jfd);
|
||||
}
|
||||
@@ -2367,7 +2323,7 @@ static int hasHotJournal(Pager *pPager){
|
||||
** Try to find a page in the cache that can be recycled.
|
||||
**
|
||||
** This routine may return SQLITE_IOERR, SQLITE_FULL or SQLITE_OK. It
|
||||
** does not set the pPager->errMask variable.
|
||||
** does not set the pPager->errCode variable.
|
||||
*/
|
||||
static int pager_recycle(Pager *pPager, int syncOk, PgHdr **ppPg){
|
||||
PgHdr *pPg;
|
||||
@@ -2512,7 +2468,7 @@ int sqlite3pager_release_memory(int nReq){
|
||||
if( rc!=SQLITE_OK ){
|
||||
/* An error occured whilst writing to the database file or
|
||||
** journal in pager_recycle(). The error is not returned to the
|
||||
** caller of this function. Instead, set the Pager.errMask variable.
|
||||
** caller of this function. Instead, set the Pager.errCode variable.
|
||||
** The error will be returned to the user (or users, in the case
|
||||
** of a shared pager cache) of the pager for which the error occured.
|
||||
*/
|
||||
@@ -2565,8 +2521,8 @@ int sqlite3pager_get(Pager *pPager, Pgno pgno, void **ppPage){
|
||||
*/
|
||||
assert( pPager!=0 );
|
||||
*ppPage = 0;
|
||||
if( pPager->errMask & ~(PAGER_ERR_FULL) ){
|
||||
return pager_errcode(pPager);
|
||||
if( pPager->errCode && pPager->errCode!=SQLITE_FULL ){
|
||||
return pPager->errCode;
|
||||
}
|
||||
|
||||
/* If this is the first page accessed, then get a SHARED lock
|
||||
@@ -2702,9 +2658,9 @@ int sqlite3pager_get(Pager *pPager, Pgno pgno, void **ppPage){
|
||||
if( pPager->nExtra>0 ){
|
||||
memset(PGHDR_TO_EXTRA(pPg, pPager), 0, pPager->nExtra);
|
||||
}
|
||||
if( pPager->errMask!=0 ){
|
||||
if( pPager->errCode ){
|
||||
sqlite3pager_unref(PGHDR_TO_DATA(pPg));
|
||||
rc = pager_errcode(pPager);
|
||||
rc = pPager->errCode;
|
||||
return rc;
|
||||
}
|
||||
if( sqlite3pager_pagecount(pPager)<(int)pgno ){
|
||||
@@ -2761,7 +2717,7 @@ void *sqlite3pager_lookup(Pager *pPager, Pgno pgno){
|
||||
|
||||
assert( pPager!=0 );
|
||||
assert( pgno!=0 );
|
||||
if( pPager->errMask & ~(PAGER_ERR_FULL) ){
|
||||
if( pPager->errCode && pPager->errCode!=SQLITE_FULL ){
|
||||
return 0;
|
||||
}
|
||||
pPg = pager_lookup(pPager, pgno);
|
||||
@@ -2859,8 +2815,8 @@ static int pager_open_journal(Pager *pPager){
|
||||
pPager->needSync = 0;
|
||||
pPager->alwaysRollback = 0;
|
||||
pPager->nRec = 0;
|
||||
if( pPager->errMask!=0 ){
|
||||
rc = pager_errcode(pPager);
|
||||
if( pPager->errCode ){
|
||||
rc = pPager->errCode;
|
||||
goto failed_to_open_journal;
|
||||
}
|
||||
pPager->origDbSize = pPager->dbSize;
|
||||
@@ -2978,8 +2934,8 @@ int sqlite3pager_write(void *pData){
|
||||
|
||||
/* Check for errors
|
||||
*/
|
||||
if( pPager->errMask ){
|
||||
return pager_errcode(pPager);
|
||||
if( pPager->errCode ){
|
||||
return pPager->errCode;
|
||||
}
|
||||
if( pPager->readOnly ){
|
||||
return SQLITE_PERM;
|
||||
@@ -3053,7 +3009,9 @@ int sqlite3pager_write(void *pData){
|
||||
*(u32*)PGHDR_TO_EXTRA(pPg, pPager) = saved;
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3pager_rollback(pPager);
|
||||
pPager->errMask |= PAGER_ERR_FULL;
|
||||
if( !pPager->errCode ){
|
||||
pager_error(pPager, SQLITE_FULL);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
pPager->nRec++;
|
||||
@@ -3100,7 +3058,9 @@ int sqlite3pager_write(void *pData){
|
||||
CODEC(pPager, pData, pPg->pgno, 0);
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3pager_rollback(pPager);
|
||||
pPager->errMask |= PAGER_ERR_FULL;
|
||||
if( !pPager->errCode ){
|
||||
pager_error(pPager, SQLITE_FULL);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
pPager->stmtNRec++;
|
||||
@@ -3260,15 +3220,15 @@ int sqlite3pager_commit(Pager *pPager){
|
||||
int rc;
|
||||
PgHdr *pPg;
|
||||
|
||||
if( pPager->errMask==PAGER_ERR_FULL ){
|
||||
if( pPager->errCode==SQLITE_FULL ){
|
||||
rc = sqlite3pager_rollback(pPager);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = SQLITE_FULL;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
if( pPager->errMask!=0 ){
|
||||
rc = pager_errcode(pPager);
|
||||
if( pPager->errCode ){
|
||||
rc = pPager->errCode;
|
||||
return rc;
|
||||
}
|
||||
if( pPager->state<PAGER_RESERVED ){
|
||||
@@ -3379,11 +3339,11 @@ int sqlite3pager_rollback(Pager *pPager){
|
||||
return rc;
|
||||
}
|
||||
|
||||
if( pPager->errMask!=0 && pPager->errMask!=PAGER_ERR_FULL ){
|
||||
if( pPager->errCode && pPager->errCode!=SQLITE_FULL ){
|
||||
if( pPager->state>=PAGER_EXCLUSIVE ){
|
||||
pager_playback(pPager);
|
||||
}
|
||||
return pager_errcode(pPager);
|
||||
return pPager->errCode;
|
||||
}
|
||||
if( pPager->state==PAGER_RESERVED ){
|
||||
int rc2;
|
||||
@@ -3397,7 +3357,7 @@ int sqlite3pager_rollback(Pager *pPager){
|
||||
}
|
||||
if( rc!=SQLITE_OK ){
|
||||
rc = SQLITE_CORRUPT_BKPT;
|
||||
pPager->errMask |= PAGER_ERR_CORRUPT;
|
||||
pager_error(pPager, SQLITE_CORRUPT);
|
||||
}
|
||||
pPager->dbSize = -1;
|
||||
return rc;
|
||||
@@ -3421,7 +3381,7 @@ int *sqlite3pager_stats(Pager *pPager){
|
||||
a[2] = pPager->mxPage;
|
||||
a[3] = pPager->dbSize;
|
||||
a[4] = pPager->state;
|
||||
a[5] = pPager->errMask;
|
||||
a[5] = pPager->errCode;
|
||||
#ifdef SQLITE_TEST
|
||||
a[6] = pPager->nHit;
|
||||
a[7] = pPager->nMiss;
|
||||
|
Reference in New Issue
Block a user