diff options
Diffstat (limited to 'src/pager.c')
-rw-r--r-- | src/pager.c | 194 |
1 files changed, 123 insertions, 71 deletions
diff --git a/src/pager.c b/src/pager.c index 88438aea3..40df13f22 100644 --- a/src/pager.c +++ b/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.228 2005/12/20 09:19:37 danielk1977 Exp $ +** @(#) $Id: pager.c,v 1.229 2005/12/30 16:28:02 danielk1977 Exp $ */ #ifndef SQLITE_OMIT_DISKIO #include "sqliteInt.h" @@ -301,12 +301,36 @@ struct Pager { /* ** 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_MEM 0x02 /* malloc() failed */ -#define PAGER_ERR_LOCK 0x04 /* error in the locking protocol */ -#define PAGER_ERR_CORRUPT 0x08 /* database or journal corruption */ -#define PAGER_ERR_DISK 0x10 /* general disk I/O error - bad hard drive? */ +#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 @@ -465,11 +489,32 @@ static int pager_errcode(Pager *pPager){ 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_MEM ) rc = SQLITE_NOMEM; 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. +*/ +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; + } + return rc; +} + #ifdef SQLITE_CHECK_PAGES /* ** Return a 32-bit hash of the page data for pPage. @@ -739,6 +784,9 @@ static int readJournalHdr( ** ** The master journal page checksum is the sum of the bytes in the master ** journal name. +** +** If zMaster is a NULL pointer (occurs for a single database transaction), +** this call is a no-op. */ static int writeMasterJournal(Pager *pPager, const char *zMaster){ int rc; @@ -862,29 +910,6 @@ static void pager_reset(Pager *pPager){ } /* -** This function is used to reset the pager after a malloc() failure. This -** doesn't work with in-memory databases. If a malloc() fails when an -** in-memory database is in use it is not possible to recover. -** -** If a transaction or statement transaction is active, it is rolled back. -** -** It is an error to call this function if any pages are in use. -*/ -#ifndef SQLITE_OMIT_GLOBALRECOVER -int sqlite3pager_reset(Pager *pPager){ - if( pPager ){ - if( pPager->nRef || MEMDB ){ - return SQLITE_ERROR; - } - pPager->errMask &= ~(PAGER_ERR_MEM); - pager_reset(pPager); - } - return SQLITE_OK; -} -#endif - - -/* ** When this routine is called, the pager has the journal file open and ** a RESERVED or EXCLUSIVE lock on the database. This routine releases ** the database lock and acquires a SHARED lock in its place. The journal @@ -1586,7 +1611,7 @@ int sqlite3pager_open( int nExtra, /* Extra bytes append to each in-memory page */ int flags /* flags controlling this file */ ){ - Pager *pPager; + Pager *pPager = 0; char *zFullPathname = 0; int nameLen; OsFile *fd; @@ -1600,17 +1625,24 @@ int sqlite3pager_open( char zTemp[SQLITE_TEMPNAME_SIZE]; SqliteTsd *pTsd = sqlite3Tsd(); + /* If malloc() has already failed return SQLITE_NOMEM. Before even + ** testing for this, set *ppPager to NULL so the caller knows the pager + ** structure was never allocated. + */ *ppPager = 0; - memset(&fd, 0, sizeof(fd)); if( sqlite3Tsd()->mallocFailed ){ return SQLITE_NOMEM; } + memset(&fd, 0, sizeof(fd)); + + /* Open the pager file and set zFullPathname to point at malloc()ed + ** memory containing the complete filename (i.e. including the directory). + */ if( zFilename && zFilename[0] ){ #ifndef SQLITE_OMIT_MEMORYDB if( strcmp(zFilename,":memory:")==0 ){ memDb = 1; zFullPathname = sqliteStrDup(""); - rc = SQLITE_OK; }else #endif { @@ -1627,28 +1659,35 @@ int sqlite3pager_open( tempFile = 1; } } - if( !zFullPathname ){ - sqlite3OsClose(&fd); - return SQLITE_NOMEM; - } - if( rc!=SQLITE_OK ){ - sqlite3OsClose(&fd); - sqliteFree(zFullPathname); - return rc; + + /* Allocate the Pager structure. As part of the same allocation, allocate + ** space for the full paths of the file, directory and journal + ** (Pager.zFilename, Pager.zDirectory and Pager.zJournal). + */ + if( zFullPathname ){ + nameLen = strlen(zFullPathname); + pPager = sqliteMalloc( sizeof(*pPager) + nameLen*3 + 30 ); } - nameLen = strlen(zFullPathname); - pPager = sqliteMalloc( sizeof(*pPager) + nameLen*3 + 30 ); - if( pPager==0 ){ + + /* If an error occured in either of the blocks above, free the memory + ** pointed to by zFullPathname, free the Pager structure and close the + ** file. Since the pager is not allocated there is no need to set + ** any Pager.errMask variables. + */ + if( !pPager || !zFullPathname || rc!=SQLITE_OK ){ sqlite3OsClose(&fd); sqliteFree(zFullPathname); - return SQLITE_NOMEM; + sqliteFree(pPager); + return ((rc==SQLITE_OK)?SQLITE_NOMEM:rc); } + TRACE3("OPEN %d %s\n", FILEHANDLEID(fd), zFullPathname); pPager->zFilename = (char*)&pPager[1]; pPager->zDirectory = &pPager->zFilename[nameLen+1]; pPager->zJournal = &pPager->zDirectory[nameLen+1]; strcpy(pPager->zFilename, zFullPathname); strcpy(pPager->zDirectory, zFullPathname); + for(i=nameLen; i>0 && pPager->zDirectory[i-1]!='/'; i--){} if( i>0 ) pPager->zDirectory[i-1] = 0; strcpy(pPager->zJournal, zFullPathname); @@ -1762,7 +1801,13 @@ void enable_simulated_io_errors(void){ /* ** Read the first N bytes from the beginning of the file into memory -** that pDest points to. No error checking is done. +** that pDest points to. +** +** No error checking is done. The rational for this is that this function +** may be called even if the file does not exist or contain a header. In +** these cases sqlite3OsRead() will return an error, to which the correct +** response is to zero the memory at pDest and continue. A real IO error +** will presumably recur and be picked up later (Todo: Think about this). */ void sqlite3pager_read_fileheader(Pager *pPager, int N, unsigned char *pDest){ memset(pDest, 0, N); @@ -1973,6 +2018,11 @@ int sqlite3pager_truncate(Pager *pPager, Pgno nPage){ ** and their memory is freed. Any attempt to use a page associated ** with this page cache after this function returns will likely ** result in a coredump. +** +** This function always succeeds. If a transaction is active an attempt +** is made to roll it back. If an error occurs during the rollback +** a hot journal may be left in the filesystem but no error is returned +** to the caller. */ int sqlite3pager_close(Pager *pPager){ PgHdr *pPg, *pNext; @@ -2037,6 +2087,9 @@ int sqlite3pager_close(Pager *pPager){ */ #ifndef SQLITE_OMIT_MEMORY_MANAGEMENT + /* Remove the pager from the linked list of pagers starting at + ** SqliteTsd.pPager. + */ if( pPager==pTsd->pPager ){ pTsd->pPager = pPager->pNext; }else{ @@ -2310,7 +2363,10 @@ static int hasHotJournal(Pager *pPager){ } /* -** Try to find a page in the cache that can be recycled. +** 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. */ static int pager_recycle(Pager *pPager, int syncOk, PgHdr **ppPg){ PgHdr *pPg; @@ -2329,8 +2385,7 @@ static int pager_recycle(Pager *pPager, int syncOk, PgHdr **ppPg){ if( pPg==0 && pPager->pFirst && syncOk && !MEMDB){ int rc = syncJournal(pPager); if( rc!=0 ){ - sqlite3pager_rollback(pPager); - return SQLITE_IOERR; + return rc; } if( pPager->fullSync ){ /* If in full-sync mode, write a new journal header into the @@ -2343,8 +2398,7 @@ static int pager_recycle(Pager *pPager, int syncOk, PgHdr **ppPg){ assert( pPager->journalOff > 0 ); rc = writeJournalHdr(pPager); if( rc!=0 ){ - sqlite3pager_rollback(pPager); - return SQLITE_IOERR; + return rc; } } pPg = pPager->pFirst; @@ -2363,8 +2417,7 @@ static int pager_recycle(Pager *pPager, int syncOk, PgHdr **ppPg){ pPg->pDirty = 0; rc = pager_write_pagelist( pPg ); if( rc!=SQLITE_OK ){ - sqlite3pager_rollback(pPager); - return SQLITE_IOERR; + return rc; } } assert( pPg->dirty==0 ); @@ -2425,9 +2478,9 @@ int sqlite3pager_release_memory(int nReq){ ** loop). */ while( SQLITE_OK==(rc = pager_recycle(p, i, &pPg)) && pPg) { - /* We've found a page to free. At this point the page has been + /* We've found a page to free. At this point the page has been ** removed from the page hash-table, free-list and synced-list - ** (pFirstSynced). It is still in the all pages (pAll) list. + ** (pFirstSynced). It is still in the all pages (pAll) list. ** Remove it from this list before freeing. ** ** Todo: Check the Pager.pStmt list to make sure this is Ok. It @@ -2435,6 +2488,7 @@ int sqlite3pager_release_memory(int nReq){ */ PgHdr *pTmp; assert( pPg ); + page_remove_from_stmt_list(pPg); if( pPg==p->pAll ){ p->pAll = pPg->pNextAll; }else{ @@ -2446,23 +2500,22 @@ int sqlite3pager_release_memory(int nReq){ } if( rc!=SQLITE_OK ){ - /* Assert that fsync() was enabled and the error was an io-error - ** or a full database. Nothing else should be able to wrong in - ** pager_recycle. + /* 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. + ** 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. */ - assert( i && (rc==SQLITE_IOERR || rc==SQLITE_FULL) ); - - /* TODO: Figure out what to do about this. The IO-error - ** belongs to the connection that is executing a transaction. - */ - assert(0); + assert( rc==SQLITE_IOERR || rc==SQLITE_FULL ); + assert( p->state>=PAGER_RESERVED ); + pager_error(p, rc); } } } - + return nReleased; } -#endif +#endif /* SQLITE_OMIT_MEMORY_MANAGEMENT */ /* ** Acquire a page. @@ -2513,7 +2566,7 @@ int sqlite3pager_get(Pager *pPager, Pgno pgno, void **ppPage){ if( !pPager->noReadlock ){ rc = pager_wait_on_lock(pPager, SHARED_LOCK); if( rc!=SQLITE_OK ){ - return rc; + return pager_error(pPager, rc); } } @@ -2538,7 +2591,7 @@ int sqlite3pager_get(Pager *pPager, Pgno pgno, void **ppPage){ if( rc!=SQLITE_OK ){ sqlite3OsUnlock(pPager->fd, NO_LOCK); pPager->state = PAGER_UNLOCK; - return rc; + return pager_error(pPager, rc); } pPager->state = PAGER_EXCLUSIVE; @@ -2567,7 +2620,7 @@ int sqlite3pager_get(Pager *pPager, Pgno pgno, void **ppPage){ */ rc = pager_playback(pPager); if( rc!=SQLITE_OK ){ - return rc; + return pager_error(pPager, rc); } } pPg = 0; @@ -2588,7 +2641,6 @@ int sqlite3pager_get(Pager *pPager, Pgno pgno, void **ppPage){ + sizeof(u32) + pPager->nExtra + MEMDB*sizeof(PgHistory) ); if( pPg==0 ){ - // pPager->errMask |= PAGER_ERR_MEM; return SQLITE_NOMEM; } memset(pPg, 0, sizeof(*pPg)); @@ -2606,7 +2658,7 @@ int sqlite3pager_get(Pager *pPager, Pgno pgno, void **ppPage){ }else{ rc = pager_recycle(pPager, 1, &pPg); if( rc!=SQLITE_OK ){ - return rc; + return pager_error(pPager, rc); } assert(pPg) ; } @@ -2662,7 +2714,7 @@ int sqlite3pager_get(Pager *pPager, Pgno pgno, void **ppPage){ if( sqlite3OsFileSize(pPager->fd,&fileSize)!=SQLITE_OK || fileSize>=pgno*pPager->pageSize ){ sqlite3pager_unref(PGHDR_TO_DATA(pPg)); - return rc; + return pager_error(pPager, rc); }else{ clear_simulated_io_error(); memset(PGHDR_TO_DATA(pPg), 0, pPager->pageSize); |