# # delete_file "sqlite/keywordhash.c" # # delete_file "sqlite/os_mac.c" # # delete_file "sqlite/os_mac.h" # # delete_file "sqlite/os_test.c" # # delete_file "sqlite/os_test.h" # # patch "sqlite/pager.c" # from [ee88fcecb081e3635c281bc09d604e934429e2f5] # to [95e24c9134a00613ca87b963a84ad62d85d5b979] # --- sqlite/pager.c +++ sqlite/pager.c @@ -18,7 +18,7 @@ ** file simultaneously, or one process from reading the database while ** another is writing. ** -** @(#) $Id: pager.c,v 1.177 2004/11/10 15:27:38 danielk1977 Exp $ +** @(#) $Id: pager.c,v 1.201 2005/03/28 18:04:28 drh Exp $ */ #include "sqliteInt.h" #include "os.h" @@ -168,6 +168,9 @@ u8 alwaysRollback; /* Disable dont_rollback() for this page */ short int nRef; /* Number of users of this page */ PgHdr *pDirty; /* Dirty pages sorted by PgHdr.pgno */ +#ifdef SQLITE_CHECK_PAGES + u32 pageHash; +#endif /* pPager->psAligned bytes of page data follow this header */ /* Pager.nExtra bytes of local data follow the page data */ }; @@ -210,9 +213,16 @@ /* ** How big to make the hash table used for locating in-memory pages -** by page number. +** by page number. This macro looks a little silly, but is evaluated +** at compile-time, not run-time (at least for gcc this is true). */ -#define N_PG_HASH 2048 +#define N_PG_HASH (\ + (MAX_PAGES>1024)?2048: \ + (MAX_PAGES>512)?1024: \ + (MAX_PAGES>256)?512: \ + (MAX_PAGES>128)?256: \ + (MAX_PAGES>64)?128:64 \ +) /* ** Hash a page number @@ -241,14 +251,17 @@ int pageSize; /* Number of bytes in a page */ int psAligned; /* pageSize rounded up to a multiple of 8 */ int nPage; /* Total number of in-memory pages */ + int nMaxPage; /* High water mark of nPage */ int nRef; /* Number of in-memory pages with PgHdr.nRef>0 */ int mxPage; /* Maximum number of pages to hold in cache */ int nHit, nMiss, nOvfl; /* Cache hits, missing, and LRU overflows */ + int nRead,nWrite; /* Database pages read/written */ void (*xCodec)(void*,void*,Pgno,int); /* Routine for en/decoding data */ void *pCodecArg; /* First argument to xCodec() */ u8 journalOpen; /* True if journal file descriptors is valid */ u8 journalStarted; /* True if header of journal is synced */ u8 useJournal; /* Use a rollback journal on this file */ + u8 noReadlock; /* Do not bother to obtain readlocks */ u8 stmtOpen; /* True if the statement subjournal is open */ u8 stmtInUse; /* True we are in a statement subtransaction */ u8 stmtAutoopen; /* Open stmt journal when main journal is opened*/ @@ -355,9 +368,14 @@ #define PAGER_MJ_PGNO(x) ((PENDING_BYTE/((x)->pageSize))+1) /* +** The maximum legal page number is (2^31 - 1). +*/ +#define PAGER_MAX_PGNO 2147483647 + +/* ** Enable reference count tracking (for debugging) here: */ -#ifdef SQLITE_TEST +#ifdef SQLITE_DEBUG int pager3_refinfo_enable = 0; static void pager_refinfo(PgHdr *p){ static int cnt = 0; @@ -444,7 +462,37 @@ return rc; } +#ifdef SQLITE_CHECK_PAGES /* +** Return a 32-bit hash of the page data for pPage. +*/ +static u32 pager_pagehash(PgHdr *pPage){ + u32 hash = 0; + int i; + unsigned char *pData = (unsigned char *)PGHDR_TO_DATA(pPage); + for(i=0; ipPager->pageSize; i++){ + hash = (hash+i)^pData[i]; + } + return hash; +} + +/* +** The CHECK_PAGE macro takes a PgHdr* as an argument. If SQLITE_CHECK_PAGES +** is defined, and NDEBUG is not defined, an assert() statement checks +** that the page is either dirty or still matches the calculated page-hash. +*/ +#define CHECK_PAGE(x) checkPage(x) +static void checkPage(PgHdr *pPg){ + Pager *pPager = pPg->pPager; + assert( !pPg->pageHash || pPager->errMask || MEMDB || pPg->dirty || + pPg->pageHash==pager_pagehash(pPg) ); +} + +#else +#define CHECK_PAGE(x) +#endif + +/* ** When this is called the journal file for pager pPager must be open. ** The master journal file name is read from the end of the file and ** written into memory obtained from sqliteMalloc(). *pzMaster is @@ -505,8 +553,9 @@ */ sqliteFree(*pzMaster); *pzMaster = 0; + }else{ + (*pzMaster)[len] = '\0'; } - (*pzMaster)[len] = '\0'; return SQLITE_OK; } @@ -781,6 +830,7 @@ */ static void pager_reset(Pager *pPager){ PgHdr *pPg, *pNext; + if( pPager->errMask ) return; for(pPg=pPager->pAll; pPg; pPg=pNext){ pNext = pPg->pNextAll; sqliteFree(pPg); @@ -802,6 +852,29 @@ } /* +** 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 @@ -833,10 +906,14 @@ pPg->inJournal = 0; pPg->dirty = 0; pPg->needSync = 0; +#ifdef SQLITE_CHECK_PAGES + pPg->pageHash = pager_pagehash(pPg); +#endif } pPager->dirtyCache = 0; pPager->nRec = 0; }else{ + assert( pPager->aInJournal==0 ); assert( pPager->dirtyCache==0 || pPager->useJournal==0 ); } rc = sqlite3OsUnlock(&pPager->fd, SHARED_LOCK); @@ -891,6 +968,12 @@ u32 cksum; /* Checksum used for sanity checking */ u8 aData[SQLITE_MAX_PAGE_SIZE]; /* Temp storage for a page */ + /* useCksum should be true for the main journal and false for + ** statement journals. Verify that this is always the case + */ + assert( jfd == (useCksum ? &pPager->jfd : &pPager->stfd) ); + + rc = read32bits(jfd, &pgno); if( rc!=SQLITE_OK ) return rc; rc = sqlite3OsRead(jfd, &aData, pPager->pageSize); @@ -925,30 +1008,45 @@ ** ** If in EXCLUSIVE state, then we update the pager cache if it exists ** and the main file. The page is then marked not dirty. + ** + ** Ticket #1171: The statement journal might contain page content that is + ** different from the page content at the start of the transaction. + ** This occurs when a page is changed prior to the start of a statement + ** then changed again within the statement. When rolling back such a + ** statement we must not write to the original database unless we know + ** for certain that original page contents are in the main rollback + ** journal. Otherwise, if a full ROLLBACK occurs after the statement + ** rollback the full ROLLBACK will not restore the page to its original + ** content. Two conditions must be met before writing to the database + ** files. (1) the database must be locked. (2) we know that the original + ** page content is in the main journal either because the page is not in + ** cache or else it is marked as needSync==0. */ pPg = pager_lookup(pPager, pgno); - assert( pPager->state>=PAGER_EXCLUSIVE || pPg ); + assert( pPager->state>=PAGER_EXCLUSIVE || pPg!=0 ); TRACE3("PLAYBACK %d page %d\n", PAGERID(pPager), pgno); - if( pPager->state>=PAGER_EXCLUSIVE ){ + if( pPager->state>=PAGER_EXCLUSIVE && (pPg==0 || pPg->needSync==0) ){ sqlite3OsSeek(&pPager->fd, (pgno-1)*(i64)pPager->pageSize); rc = sqlite3OsWrite(&pPager->fd, aData, pPager->pageSize); + if( pPg ) pPg->dirty = 0; } if( pPg ){ - /* No page should ever be rolled back that is in use, except for page - ** 1 which is held in use in order to keep the lock on the database - ** active. + /* No page should ever be explicitly rolled back that is in use, except + ** for page 1 which is held in use in order to keep the lock on the + ** database active. However such a page may be rolled back as a result + ** of an internal error resulting in an automatic call to + ** sqlite3pager_rollback(). */ void *pData; - assert( pPg->nRef==0 || pPg->pgno==1 ); + /* assert( pPg->nRef==0 || pPg->pgno==1 ); */ pData = PGHDR_TO_DATA(pPg); memcpy(pData, aData, pPager->pageSize); if( pPager->xDestructor ){ /*** FIX ME: Should this be xReinit? ***/ pPager->xDestructor(pData, pPager->pageSize); } - if( pPager->state>=PAGER_EXCLUSIVE ){ - pPg->dirty = 0; - pPg->needSync = 0; - } +#ifdef SQLITE_CHECK_PAGES + pPg->pageHash = pager_pagehash(pPg); +#endif CODEC(pPager, pData, pPg->pgno, 3); } return rc; @@ -1005,6 +1103,7 @@ ** so, return without deleting the master journal file. */ OsFile journal; + int c; memset(&journal, 0, sizeof(journal)); rc = sqlite3OsOpenReadOnly(zJournal, &journal); @@ -1018,7 +1117,9 @@ goto delmaster_out; } - if( zMasterPtr && !strcmp(zMasterPtr, zMaster) ){ + c = zMasterPtr!=0 && strcmp(zMasterPtr, zMaster)==0; + sqliteFree(zMasterPtr); + if( c ){ /* We have a match. Do not delete the master journal file. */ goto delmaster_out; } @@ -1073,6 +1174,9 @@ } pPg->needSync = 0; pPg->dirty = 0; +#ifdef SQLITE_CHECK_PAGES + pPg->pageHash = pager_pagehash(pPg); +#endif } return rc; } @@ -1082,6 +1186,7 @@ ** indicated. */ static int pager_truncate(Pager *pPager, int nPage){ + assert( pPager->state>=PAGER_EXCLUSIVE ); return sqlite3OsTruncate(&pPager->fd, pPager->pageSize*(i64)nPage); } @@ -1201,7 +1306,8 @@ /* If this is the first header read from the journal, truncate the ** database file back to it's original size. */ - if( pPager->journalOff==JOURNAL_HDR_SZ(pPager) ){ + if( pPager->state>=PAGER_EXCLUSIVE && + pPager->journalOff==JOURNAL_HDR_SZ(pPager) ){ assert( pPager->origDbSize==0 || pPager->origDbSize==mxPg ); rc = pager_truncate(pPager, mxPg); if( rc!=SQLITE_OK ){ @@ -1242,11 +1348,10 @@ } if( zMaster ){ /* If there was a master journal and this routine will return true, - ** see if it is possible to delete the master journal. If errors - ** occur during this process, ignore them. + ** see if it is possible to delete the master journal. */ if( rc==SQLITE_OK ){ - pager_delmaster(zMaster); + rc = pager_delmaster(zMaster); } sqliteFree(zMaster); } @@ -1300,10 +1405,11 @@ hdrOff = szJ; } - /* Truncate the database back to its original size. */ - rc = pager_truncate(pPager, pPager->stmtSize); + if( pPager->state>=PAGER_EXCLUSIVE ){ + rc = pager_truncate(pPager, pPager->stmtSize); + } pPager->dbSize = pPager->stmtSize; /* Figure out how many records are in the statement journal. @@ -1426,11 +1532,13 @@ ** Numeric values associated with these states are OFF==1, NORMAL=2, ** and FULL=3. */ +#ifndef SQLITE_OMIT_PAGER_PRAGMAS void sqlite3pager_set_safety_level(Pager *pPager, int level){ pPager->noSync = level==1 || pPager->tempFile; pPager->fullSync = level==3 && !pPager->tempFile; if( pPager->noSync ) pPager->needSync = 0; } +#endif /* ** Open a temporary file. Write the name of the file into zName @@ -1470,7 +1578,7 @@ Pager **ppPager, /* Return the Pager structure here */ const char *zFilename, /* Name of the database file to open */ int nExtra, /* Extra bytes append to each in-memory page */ - int useJournal /* TRUE to use a rollback journal on this file */ + int flags /* flags controlling this file */ ){ Pager *pPager; char *zFullPathname = 0; @@ -1481,6 +1589,8 @@ int tempFile = 0; int memDb = 0; int readOnly = 0; + int useJournal = (flags & PAGER_OMIT_JOURNAL)==0; + int noReadlock = (flags & PAGER_NO_READLOCK)!=0; char zTemp[SQLITE_TEMPNAME_SIZE]; *ppPager = 0; @@ -1543,6 +1653,7 @@ #endif pPager->journalOpen = 0; pPager->useJournal = useJournal && !memDb; + pPager->noReadlock = noReadlock && readOnly; pPager->stmtOpen = 0; pPager->stmtInUse = 0; pPager->nRef = 0; @@ -1552,6 +1663,7 @@ pPager->stmtSize = 0; pPager->stmtJSize = 0; pPager->nPage = 0; + pPager->nMaxPage = 0; pPager->mxPage = 100; pPager->state = PAGER_UNLOCK; pPager->errMask = 0; @@ -1715,6 +1827,7 @@ unlinkHashChain(pPager, pPg); } +#ifndef SQLITE_OMIT_MEMORYDB /* ** This routine is used to truncate an in-memory database. Delete ** all pages whose pgno is larger than pPager->dbSize and is unreferenced. @@ -1740,8 +1853,42 @@ } } } +#else +#define memoryTruncate(p) +#endif /* +** Try to obtain a lock on a file. Invoke the busy callback if the lock +** is currently not available. Repeate until the busy callback returns +** false or until the lock succeeds. +** +** Return SQLITE_OK on success and an error code if we cannot obtain +** the lock. +*/ +static int pager_wait_on_lock(Pager *pPager, int locktype){ + int rc; + assert( PAGER_SHARED==SHARED_LOCK ); + assert( PAGER_RESERVED==RESERVED_LOCK ); + assert( PAGER_EXCLUSIVE==EXCLUSIVE_LOCK ); + if( pPager->state>=locktype ){ + rc = SQLITE_OK; + }else{ + int busy = 1; + BusyHandler *pH; + do { + rc = sqlite3OsLock(&pPager->fd, locktype); + }while( rc==SQLITE_BUSY && + (pH = pPager->pBusyHandler)!=0 && + pH->xFunc && pH->xFunc(pH->pArg, busy++) + ); + if( rc==SQLITE_OK ){ + pPager->state = locktype; + } + } + return rc; +} + +/* ** Truncate the file to the number of pages specified. */ int sqlite3pager_truncate(Pager *pPager, Pgno nPage){ @@ -1763,6 +1910,13 @@ if( rc!=SQLITE_OK ){ return rc; } + + /* Get an exclusive lock on the database before truncating. */ + rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK); + if( rc!=SQLITE_OK ){ + return rc; + } + rc = pager_truncate(pPager, nPage); if( rc==SQLITE_OK ){ pPager->dbSize = nPage; @@ -1785,11 +1939,23 @@ case PAGER_RESERVED: case PAGER_SYNCED: case PAGER_EXCLUSIVE: { + /* We ignore any IO errors that occur during the rollback + ** operation. So disable IO error simulation so that testing + ** works more easily. + */ +#if defined(SQLITE_TEST) && (defined(OS_UNIX) || defined(OS_WIN)) + extern int sqlite3_io_error_pending; + int ioerr_cnt = sqlite3_io_error_pending; + sqlite3_io_error_pending = -1; +#endif sqlite3pager_rollback(pPager); +#if defined(SQLITE_TEST) && (defined(OS_UNIX) || defined(OS_WIN)) + sqlite3_io_error_pending = ioerr_cnt; +#endif if( !MEMDB ){ sqlite3OsUnlock(&pPager->fd, NO_LOCK); } - assert( pPager->journalOpen==0 ); + assert( pPager->errMask || pPager->journalOpen==0 ); break; } case PAGER_SHARED: { @@ -1816,19 +1982,21 @@ sqliteFree(pPg); } TRACE2("CLOSE %d\n", PAGERID(pPager)); + assert( pPager->errMask || (pPager->journalOpen==0 && pPager->stmtOpen==0) ); + if( pPager->journalOpen ){ + sqlite3OsClose(&pPager->jfd); + } + sqliteFree(pPager->aInJournal); + if( pPager->stmtOpen ){ + sqlite3OsClose(&pPager->stfd); + } sqlite3OsClose(&pPager->fd); - assert( pPager->journalOpen==0 ); /* Temp files are automatically deleted by the OS ** if( pPager->tempFile ){ ** sqlite3OsDelete(pPager->zFilename); ** } */ - if( pPager->zFilename!=(char*)&pPager[1] ){ - assert( 0 ); /* Cannot happen */ - sqliteFree(pPager->zFilename); - sqliteFree(pPager->zJournal); - sqliteFree(pPager->zDirectory); - } + sqliteFree(pPager); return SQLITE_OK; } @@ -1873,7 +2041,7 @@ pPg->nRef++; REFINFO(pPg); } -#ifdef SQLITE_TEST +#ifdef SQLITE_DEBUG static void page_ref(PgHdr *pPg){ if( pPg->nRef==0 ){ _page_ref(pPg); @@ -1988,37 +2156,6 @@ } /* -** Try to obtain a lock on a file. Invoke the busy callback if the lock -** is currently not available. Repeate until the busy callback returns -** false or until the lock succeeds. -** -** Return SQLITE_OK on success and an error code if we cannot obtain -** the lock. -*/ -static int pager_wait_on_lock(Pager *pPager, int locktype){ - int rc; - assert( PAGER_SHARED==SHARED_LOCK ); - assert( PAGER_RESERVED==RESERVED_LOCK ); - assert( PAGER_EXCLUSIVE==EXCLUSIVE_LOCK ); - if( pPager->state>=locktype ){ - rc = SQLITE_OK; - }else{ - int busy = 1; - do { - rc = sqlite3OsLock(&pPager->fd, locktype); - }while( rc==SQLITE_BUSY && - pPager->pBusyHandler && - pPager->pBusyHandler->xFunc && - pPager->pBusyHandler->xFunc(pPager->pBusyHandler->pArg, busy++) - ); - if( rc==SQLITE_OK ){ - pPager->state = locktype; - } - } - return rc; -} - -/* ** Given a list of pages (connected by the PgHdr.pDirty pointer) write ** every one of those pages out to the database file and mark them all ** as clean. @@ -2064,6 +2201,7 @@ TRACE3("STORE %d page %d\n", PAGERID(pPager), pList->pgno); rc = sqlite3OsWrite(&pPager->fd, PGHDR_TO_DATA(pList), pPager->pageSize); CODEC(pPager, PGHDR_TO_DATA(pList), pList->pgno, 0); + pPager->nWrite++; } #ifndef NDEBUG else{ @@ -2072,6 +2210,9 @@ #endif if( rc ) return rc; pList->dirty = 0; +#ifdef SQLITE_CHECK_PAGES + pList->pageHash = pager_pagehash(pList); +#endif pList = pList->pDirty; } return SQLITE_OK; @@ -2095,6 +2236,26 @@ } /* +** Return TRUE if there is a hot journal on the given pager. +** A hot journal is one that needs to be played back. +** +** If the current size of the database file is 0 but a journal file +** exists, that is probably an old journal left over from a prior +** database with the same name. Just delete the journal. +*/ +static int hasHotJournal(Pager *pPager){ + if( !pPager->useJournal ) return 0; + if( !sqlite3OsFileExists(pPager->zJournal) ) return 0; + if( sqlite3OsCheckReservedLock(&pPager->fd) ) return 0; + if( sqlite3pager_pagecount(pPager)==0 ){ + sqlite3OsDelete(pPager->zJournal); + return 0; + }else{ + return 1; + } +} + +/* ** Acquire a page. ** ** A read lock on the disk file is obtained when the first page is acquired. @@ -2121,10 +2282,16 @@ PgHdr *pPg; int rc; + /* The maximum page number is 2^31. Return SQLITE_CORRUPT if a page + ** number greater than this, or zero, is requested. + */ + if( pgno>PAGER_MAX_PGNO || pgno==0 ){ + return SQLITE_CORRUPT; + } + /* Make sure we have not hit any critical errors. */ assert( pPager!=0 ); - assert( pgno!=0 ); *ppPage = 0; if( pPager->errMask & ~(PAGER_ERR_FULL) ){ return pager_errcode(pPager); @@ -2134,18 +2301,17 @@ ** on the database file. */ if( pPager->nRef==0 && !MEMDB ){ - rc = pager_wait_on_lock(pPager, SHARED_LOCK); - if( rc!=SQLITE_OK ){ - return rc; + if( !pPager->noReadlock ){ + rc = pager_wait_on_lock(pPager, SHARED_LOCK); + if( rc!=SQLITE_OK ){ + return rc; + } } /* 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( pPager->useJournal && - sqlite3OsFileExists(pPager->zJournal) && - !sqlite3OsCheckReservedLock(&pPager->fd) - ){ + if( hasHotJournal(pPager) ){ int rc; /* Get an EXCLUSIVE lock on the database file. At this point it is @@ -2213,9 +2379,6 @@ + sizeof(u32) + pPager->nExtra + MEMDB*sizeof(PgHistory) ); if( pPg==0 ){ - if( !MEMDB ){ - pager_unwritelock(pPager); - } pPager->errMask |= PAGER_ERR_MEM; return SQLITE_NOMEM; } @@ -2227,6 +2390,10 @@ pPg->pNextAll = pPager->pAll; pPager->pAll = pPg; pPager->nPage++; + if( pPager->nPage>pPager->nMaxPage ){ + assert( pPager->nMaxPage==(pPager->nPage-1) ); + pPager->nMaxPage++; + } }else{ /* Find a page to recycle. Try to locate a page that does not ** require us to do an fsync() on the journal. @@ -2323,13 +2490,12 @@ if( pPager->nExtra>0 ){ memset(PGHDR_TO_EXTRA(pPg, pPager), 0, pPager->nExtra); } - sqlite3pager_pagecount(pPager); if( pPager->errMask!=0 ){ sqlite3pager_unref(PGHDR_TO_DATA(pPg)); rc = pager_errcode(pPager); return rc; } - if( pPager->dbSize<(int)pgno ){ + if( sqlite3pager_pagecount(pPager)<(int)pgno ){ memset(PGHDR_TO_DATA(pPg), 0, pPager->pageSize); }else{ int rc; @@ -2347,8 +2513,13 @@ }else{ memset(PGHDR_TO_DATA(pPg), 0, pPager->pageSize); } + }else{ + pPager->nRead++; } } +#ifdef SQLITE_CHECK_PAGES + pPg->pageHash = pager_pagehash(pPg); +#endif }else{ /* The requested page is in the page cache. */ pPager->nHit++; @@ -2401,6 +2572,8 @@ pPg->nRef--; REFINFO(pPg); + CHECK_PAGE(pPg); + /* When the number of references to a page reach 0, call the ** destructor and add the page to the freelist. */ @@ -2447,6 +2620,7 @@ assert( pPager->state>=PAGER_RESERVED ); assert( pPager->journalOpen==0 ); assert( pPager->useJournal ); + assert( pPager->aInJournal==0 ); sqlite3pager_pagecount(pPager); pPager->aInJournal = sqliteMalloc( pPager->dbSize/8 + 1 ); if( pPager->aInJournal==0 ){ @@ -2460,6 +2634,8 @@ if( rc!=SQLITE_OK ){ goto failed_to_open_journal; } + SET_FULLSYNC(pPager->jfd, pPager->fullSync); + SET_FULLSYNC(pPager->fd, pPager->fullSync); sqlite3OsOpenDirectory(pPager->zDirectory, &pPager->jfd); pPager->journalOpen = 1; pPager->journalStarted = 0; @@ -2468,7 +2644,7 @@ pPager->nRec = 0; if( pPager->errMask!=0 ){ rc = pager_errcode(pPager); - return rc; + goto failed_to_open_journal; } pPager->origDbSize = pPager->dbSize; @@ -2532,11 +2708,7 @@ pPager->state = PAGER_EXCLUSIVE; pPager->origDbSize = pPager->dbSize; }else{ - if( SQLITE_BUSY_RESERVED_LOCK || exFlag ){ - rc = pager_wait_on_lock(pPager, RESERVED_LOCK); - }else{ - rc = sqlite3OsLock(&pPager->fd, RESERVED_LOCK); - } + rc = sqlite3OsLock(&pPager->fd, RESERVED_LOCK); if( rc==SQLITE_OK ){ pPager->state = PAGER_RESERVED; if( exFlag ){ @@ -2589,6 +2761,8 @@ assert( !pPager->setMaster ); + CHECK_PAGE(pPg); + /* Mark the page as dirty. If the page has already been written ** to the journal then we can return right away. */ @@ -2727,6 +2901,7 @@ return pPg->dirty; } +#ifndef SQLITE_OMIT_VACUUM /* ** Replace the content of a single page with the information in the third ** argument. @@ -2745,6 +2920,7 @@ } return rc; } +#endif /* ** A call to this routine tells the pager that it is not necessary to @@ -2790,6 +2966,9 @@ }else{ TRACE3("DONT_WRITE page %d of %d\n", pgno, PAGERID(pPager)); pPg->dirty = 0; +#ifdef SQLITE_CHECK_PAGES + pPg->pageHash = pager_pagehash(pPg); +#endif } } } @@ -2825,6 +3004,7 @@ } +#ifndef SQLITE_OMIT_MEMORYDB /* ** Clear a PgHistory block */ @@ -2834,6 +3014,9 @@ pHist->pOrig = 0; pHist->pStmt = 0; } +#else +#define clearHistory(x) +#endif /* ** Commit all changes to the database and release the write lock. @@ -2972,13 +3155,11 @@ return pager_errcode(pPager); } if( pPager->state==PAGER_RESERVED ){ - int rc2, rc3; + int rc2; rc = pager_reload_cache(pPager); - rc2 = pager_truncate(pPager, pPager->origDbSize); - rc3 = pager_unwritelock(pPager); + rc2 = pager_unwritelock(pPager); if( rc==SQLITE_OK ){ rc = rc2; - if( rc3 ) rc = rc3; } }else{ rc = pager_playback(pPager); @@ -3003,7 +3184,7 @@ ** This routine is used for testing and analysis only. */ int *sqlite3pager_stats(Pager *pPager){ - static int a[9]; + static int a[11]; a[0] = pPager->nRef; a[1] = pPager->nPage; a[2] = pPager->mxPage; @@ -3013,6 +3194,8 @@ a[6] = pPager->nHit; a[7] = pPager->nMiss; a[8] = pPager->nOvfl; + a[9] = pPager->nRead; + a[10] = pPager->nWrite; return a; } @@ -3296,8 +3479,10 @@ ** meta-data associated with page pData (i.e. data stored in the nExtra bytes ** allocated along with the page) is the responsibility of the caller. ** -** A transaction must be active when this routine is called, however it is -** illegal to call this routine if a statment transaction is active. +** A transaction must be active when this routine is called. It used to be +** required that a statement transaction was not active, but this restriction +** has been removed (CREATE INDEX needs to move a page when a statement +** transaction is active). */ int sqlite3pager_movepage(Pager *pPager, void *pData, Pgno pgno){ PgHdr *pPg = DATA_TO_PGHDR(pData); @@ -3305,7 +3490,6 @@ int h; Pgno needSyncPgno = 0; - assert( !pPager->stmtInUse ); assert( pPg->nRef>0 ); TRACE5("MOVE %d page %d (needSync=%d) moves to %d\n", @@ -3315,6 +3499,7 @@ needSyncPgno = pPg->pgno; assert( pPg->inJournal ); assert( pPg->dirty ); + assert( pPager->needSync ); } /* Unlink pPg from it's hash-chain */ @@ -3334,6 +3519,7 @@ assert( pPgOld->inJournal ); pPg->inJournal = 1; pPg->needSync = 1; + assert( pPager->needSync ); } } @@ -3357,11 +3543,16 @@ ** Currently, no such page exists in the page-cache and the ** Pager.aInJournal bit has been set. This needs to be remedied by loading ** the page into the pager-cache and setting the PgHdr.needSync flag. + ** + ** The sqlite3pager_get() call may cause the journal to sync. So make + ** sure the Pager.needSync flag is set too. */ int rc; void *pNeedSync; + assert( pPager->needSync ); rc = sqlite3pager_get(pPager, needSyncPgno, &pNeedSync); if( rc!=SQLITE_OK ) return rc; + pPager->needSync = 1; DATA_TO_PGHDR(pNeedSync)->needSync = 1; DATA_TO_PGHDR(pNeedSync)->inJournal = 1; DATA_TO_PGHDR(pNeedSync)->dirty = 1; @@ -3387,7 +3578,7 @@ } #endif -#ifdef SQLITE_TEST +#ifdef SQLITE_DEBUG /* ** Print a listing of all referenced pages and their ref count. */