| Index: third_party/sqlite/sqlite-src-3100200/src/pager.c
|
| diff --git a/third_party/sqlite/src/src/pager.c b/third_party/sqlite/sqlite-src-3100200/src/pager.c
|
| similarity index 95%
|
| copy from third_party/sqlite/src/src/pager.c
|
| copy to third_party/sqlite/sqlite-src-3100200/src/pager.c
|
| index 34fa50fe8cf582eb2a9a26b0837414aaad345cee..2c904d2df1f0f5ca08f9566851b5b9a9e319b9de 100644
|
| --- a/third_party/sqlite/src/src/pager.c
|
| +++ b/third_party/sqlite/sqlite-src-3100200/src/pager.c
|
| @@ -456,9 +456,9 @@ struct PagerSavepoint {
|
| /*
|
| ** Bits of the Pager.doNotSpill flag. See further description below.
|
| */
|
| -#define SPILLFLAG_OFF 0x01 /* Never spill cache. Set via pragma */
|
| -#define SPILLFLAG_ROLLBACK 0x02 /* Current rolling back, so do not spill */
|
| -#define SPILLFLAG_NOSYNC 0x04 /* Spill is ok, but do not sync */
|
| +#define SPILLFLAG_OFF 0x01 /* Never spill cache. Set via pragma */
|
| +#define SPILLFLAG_ROLLBACK 0x02 /* Current rolling back, so do not spill */
|
| +#define SPILLFLAG_NOSYNC 0x04 /* Spill is ok, but do not sync */
|
|
|
| /*
|
| ** An open page cache is an instance of struct Pager. A description of
|
| @@ -540,11 +540,11 @@ struct PagerSavepoint {
|
| ** while it is being traversed by code in pager_playback(). The SPILLFLAG_OFF
|
| ** case is a user preference.
|
| **
|
| -** If the SPILLFLAG_NOSYNC bit is set, writing to the database from pagerStress()
|
| -** is permitted, but syncing the journal file is not. This flag is set
|
| -** by sqlite3PagerWrite() when the file-system sector-size is larger than
|
| -** the database page-size in order to prevent a journal sync from happening
|
| -** in between the journalling of two pages on the same sector.
|
| +** If the SPILLFLAG_NOSYNC bit is set, writing to the database from
|
| +** pagerStress() is permitted, but syncing the journal file is not.
|
| +** This flag is set by sqlite3PagerWrite() when the file-system sector-size
|
| +** is larger than the database page-size in order to prevent a journal sync
|
| +** from happening in between the journalling of two pages on the same sector.
|
| **
|
| ** subjInMemory
|
| **
|
| @@ -646,6 +646,8 @@ struct Pager {
|
| u8 setMaster; /* True if a m-j name has been written to jrnl */
|
| u8 doNotSpill; /* Do not spill the cache when non-zero */
|
| u8 subjInMemory; /* True to use in-memory sub-journals */
|
| + u8 bUseFetch; /* True to use xFetch() */
|
| + u8 hasHeldSharedLock; /* True if a shared lock has ever been held */
|
| Pgno dbSize; /* Number of pages in the database */
|
| Pgno dbOrigSize; /* dbSize before the current transaction */
|
| Pgno dbFileSize; /* Number of pages in the database file */
|
| @@ -663,9 +665,9 @@ struct Pager {
|
| sqlite3_backup *pBackup; /* Pointer to list of ongoing backup processes */
|
| PagerSavepoint *aSavepoint; /* Array of active savepoints */
|
| int nSavepoint; /* Number of elements in aSavepoint[] */
|
| + u32 iDataVersion; /* Changes whenever database content changes */
|
| char dbFileVers[16]; /* Changes whenever database file changes */
|
|
|
| - u8 bUseFetch; /* True to use xFetch() */
|
| int nMmapOut; /* Number of mmap pages currently outstanding */
|
| sqlite3_int64 szMmap; /* Desired maximum mmap size */
|
| PgHdr *pMmapFreelist; /* List of free mmap page headers (pDirty) */
|
| @@ -806,7 +808,7 @@ static const unsigned char aJournalMagic[] = {
|
| **
|
| ** if( pPager->jfd->pMethods ){ ...
|
| */
|
| -#define isOpen(pFd) ((pFd)->pMethods)
|
| +#define isOpen(pFd) ((pFd)->pMethods!=0)
|
|
|
| /*
|
| ** Return true if this pager uses a write-ahead log instead of the usual
|
| @@ -1029,19 +1031,21 @@ static int subjRequiresPage(PgHdr *pPg){
|
| int i;
|
| for(i=0; i<pPager->nSavepoint; i++){
|
| p = &pPager->aSavepoint[i];
|
| - if( p->nOrig>=pgno && 0==sqlite3BitvecTest(p->pInSavepoint, pgno) ){
|
| + if( p->nOrig>=pgno && 0==sqlite3BitvecTestNotNull(p->pInSavepoint, pgno) ){
|
| return 1;
|
| }
|
| }
|
| return 0;
|
| }
|
|
|
| +#ifdef SQLITE_DEBUG
|
| /*
|
| ** Return true if the page is already in the journal file.
|
| */
|
| static int pageInJournal(Pager *pPager, PgHdr *pPg){
|
| return sqlite3BitvecTest(pPager->pInJournal, pPg->pgno);
|
| }
|
| +#endif
|
|
|
| /*
|
| ** Read a 32-bit integer from the given file descriptor. Store the integer
|
| @@ -1653,7 +1657,8 @@ static int writeMasterJournal(Pager *pPager, const char *zMaster){
|
| || (0 != (rc = sqlite3OsWrite(pPager->jfd, zMaster, nMaster, iHdrOff+4)))
|
| || (0 != (rc = write32bits(pPager->jfd, iHdrOff+4+nMaster, nMaster)))
|
| || (0 != (rc = write32bits(pPager->jfd, iHdrOff+4+nMaster+4, cksum)))
|
| - || (0 != (rc = sqlite3OsWrite(pPager->jfd, aJournalMagic, 8, iHdrOff+4+nMaster+8)))
|
| + || (0 != (rc = sqlite3OsWrite(pPager->jfd, aJournalMagic, 8,
|
| + iHdrOff+4+nMaster+8)))
|
| ){
|
| return rc;
|
| }
|
| @@ -1681,11 +1686,20 @@ static int writeMasterJournal(Pager *pPager, const char *zMaster){
|
| ** Discard the entire contents of the in-memory page-cache.
|
| */
|
| static void pager_reset(Pager *pPager){
|
| + pPager->iDataVersion++;
|
| sqlite3BackupRestart(pPager->pBackup);
|
| sqlite3PcacheClear(pPager->pPCache);
|
| }
|
|
|
| /*
|
| +** Return the pPager->iDataVersion value
|
| +*/
|
| +u32 sqlite3PagerDataVersion(Pager *pPager){
|
| + assert( pPager->eState>PAGER_OPEN );
|
| + return pPager->iDataVersion;
|
| +}
|
| +
|
| +/*
|
| ** Free all structures in the Pager.aSavepoint[] array and set both
|
| ** Pager.aSavepoint and Pager.nSavepoint to zero. Close the sub-journal
|
| ** if it is open and the pager is not in exclusive mode.
|
| @@ -2102,6 +2116,20 @@ static void pagerReportSize(Pager *pPager){
|
| # define pagerReportSize(X) /* No-op if we do not support a codec */
|
| #endif
|
|
|
| +#ifdef SQLITE_HAS_CODEC
|
| +/*
|
| +** Make sure the number of reserved bits is the same in the destination
|
| +** pager as it is in the source. This comes up when a VACUUM changes the
|
| +** number of reserved bits to the "optimal" amount.
|
| +*/
|
| +void sqlite3PagerAlignReserve(Pager *pDest, Pager *pSrc){
|
| + if( pDest->nReserve!=pSrc->nReserve ){
|
| + pDest->nReserve = pSrc->nReserve;
|
| + pagerReportSize(pDest);
|
| + }
|
| +}
|
| +#endif
|
| +
|
| /*
|
| ** Read a single page from either the journal file (if isMainJrnl==1) or
|
| ** from the sub-journal (if isMainJrnl==0) and playback that page.
|
| @@ -2204,7 +2232,7 @@ static int pager_playback_one_page(
|
| }
|
| }
|
|
|
| - /* If this page has already been played by before during the current
|
| + /* If this page has already been played back before during the current
|
| ** rollback, then don't bother to play it back again.
|
| */
|
| if( pDone && (rc = sqlite3BitvecSet(pDone, pgno))!=SQLITE_OK ){
|
| @@ -2305,7 +2333,7 @@ static int pager_playback_one_page(
|
| assert( isSavepnt );
|
| assert( (pPager->doNotSpill & SPILLFLAG_ROLLBACK)==0 );
|
| pPager->doNotSpill |= SPILLFLAG_ROLLBACK;
|
| - rc = sqlite3PagerAcquire(pPager, pgno, &pPg, 1);
|
| + rc = sqlite3PagerGet(pPager, pgno, &pPg, 1);
|
| assert( (pPager->doNotSpill & SPILLFLAG_ROLLBACK)!=0 );
|
| pPager->doNotSpill &= ~SPILLFLAG_ROLLBACK;
|
| if( rc!=SQLITE_OK ) return rc;
|
| @@ -2899,7 +2927,7 @@ static int readDbPage(PgHdr *pPg, u32 iFrame){
|
| **
|
| ** For an encrypted database, the situation is more complex: bytes
|
| ** 24..39 of the database are white noise. But the probability of
|
| - ** white noising equaling 16 bytes of 0xff is vanishingly small so
|
| + ** white noise equaling 16 bytes of 0xff is vanishingly small so
|
| ** we should still be ok.
|
| */
|
| memset(pPager->dbFileVers, 0xff, sizeof(pPager->dbFileVers));
|
| @@ -3033,9 +3061,7 @@ static int pagerWalFrames(
|
| ){
|
| int rc; /* Return code */
|
| int nList; /* Number of pages in pList */
|
| -#if defined(SQLITE_DEBUG) || defined(SQLITE_CHECK_PAGES)
|
| PgHdr *p; /* For looping over pages */
|
| -#endif
|
|
|
| assert( pPager->pWal );
|
| assert( pList );
|
| @@ -3052,7 +3078,6 @@ static int pagerWalFrames(
|
| ** any pages with page numbers greater than nTruncate into the WAL file.
|
| ** They will never be read by any client. So remove them from the pDirty
|
| ** list here. */
|
| - PgHdr *p;
|
| PgHdr **ppNext = &pList;
|
| nList = 0;
|
| for(p=pList; (*ppNext = p)!=0; p=p->pDirty){
|
| @@ -3072,7 +3097,6 @@ static int pagerWalFrames(
|
| pPager->pageSize, pList, nTruncate, isCommit, pPager->walSyncFlags
|
| );
|
| if( rc==SQLITE_OK && pPager->pBackup ){
|
| - PgHdr *p;
|
| for(p=pList; p; p=p->pDirty){
|
| sqlite3BackupUpdate(pPager->pBackup, p->pgno, (u8 *)p->pData);
|
| }
|
| @@ -3142,11 +3166,10 @@ static int pagerPagecount(Pager *pPager, Pgno *pnPage){
|
| assert( pPager->eLock>=SHARED_LOCK );
|
| nPage = sqlite3WalDbsize(pPager->pWal);
|
|
|
| - /* If the database size was not available from the WAL sub-system,
|
| - ** determine it based on the size of the database file. If the size
|
| - ** of the database file is not an integer multiple of the page-size,
|
| - ** round down to the nearest page. Except, any file larger than 0
|
| - ** bytes in size is considered to contain at least one page.
|
| + /* If the number of pages in the database is not available from the
|
| + ** WAL sub-system, determine the page counte based on the size of
|
| + ** the database file. If the size of the database file is not an
|
| + ** integer multiple of the page-size, round up the result.
|
| */
|
| if( nPage==0 ){
|
| i64 n = 0; /* Size of db file in bytes */
|
| @@ -3369,13 +3392,22 @@ static int pagerPlaybackSavepoint(Pager *pPager, PagerSavepoint *pSavepoint){
|
| }
|
|
|
| /*
|
| -** Change the maximum number of in-memory pages that are allowed.
|
| +** Change the maximum number of in-memory pages that are allowed
|
| +** before attempting to recycle clean and unused pages.
|
| */
|
| void sqlite3PagerSetCachesize(Pager *pPager, int mxPage){
|
| sqlite3PcacheSetCachesize(pPager->pPCache, mxPage);
|
| }
|
|
|
| /*
|
| +** Change the maximum number of in-memory pages that are allowed
|
| +** before attempting to spill pages to journal.
|
| +*/
|
| +int sqlite3PagerSetSpillsize(Pager *pPager, int mxPage){
|
| + return sqlite3PcacheSetSpillsize(pPager->pPCache, mxPage);
|
| +}
|
| +
|
| +/*
|
| ** Invoke SQLITE_FCNTL_MMAP_SIZE based on the current value of szMmap.
|
| */
|
| static void pagerFixMaplimit(Pager *pPager){
|
| @@ -3887,7 +3919,7 @@ static int pagerAcquireMapPage(
|
| PgHdr **ppPage /* OUT: Acquired page object */
|
| ){
|
| PgHdr *p; /* Memory mapped page to return */
|
| -
|
| +
|
| if( pPager->pMmapFreelist ){
|
| *ppPage = p = pPager->pMmapFreelist;
|
| pPager->pMmapFreelist = p->pDirty;
|
| @@ -4311,8 +4343,6 @@ static int openSubJournal(Pager *pPager){
|
|
|
| /*
|
| ** Append a record of the current state of page pPg to the sub-journal.
|
| -** It is the callers responsibility to use subjRequiresPage() to check
|
| -** that it is really required before calling this function.
|
| **
|
| ** If successful, set the bit corresponding to pPg->pgno in the bitvecs
|
| ** for all open savepoints before returning.
|
| @@ -4359,6 +4389,13 @@ static int subjournalPage(PgHdr *pPg){
|
| }
|
| return rc;
|
| }
|
| +static int subjournalPageIfRequired(PgHdr *pPg){
|
| + if( subjRequiresPage(pPg) ){
|
| + return subjournalPage(pPg);
|
| + }else{
|
| + return SQLITE_OK;
|
| + }
|
| +}
|
|
|
| /*
|
| ** This function is called by the pcache layer when it has reached some
|
| @@ -4416,9 +4453,7 @@ static int pagerStress(void *p, PgHdr *pPg){
|
| pPg->pDirty = 0;
|
| if( pagerUseWal(pPager) ){
|
| /* Write a single frame for this page to the log. */
|
| - if( subjRequiresPage(pPg) ){
|
| - rc = subjournalPage(pPg);
|
| - }
|
| + rc = subjournalPageIfRequired(pPg);
|
| if( rc==SQLITE_OK ){
|
| rc = pagerWalFrames(pPager, pPg, 0, 0);
|
| }
|
| @@ -4431,39 +4466,6 @@ static int pagerStress(void *p, PgHdr *pPg){
|
| rc = syncJournal(pPager, 1);
|
| }
|
|
|
| - /* If the page number of this page is larger than the current size of
|
| - ** the database image, it may need to be written to the sub-journal.
|
| - ** This is because the call to pager_write_pagelist() below will not
|
| - ** actually write data to the file in this case.
|
| - **
|
| - ** Consider the following sequence of events:
|
| - **
|
| - ** BEGIN;
|
| - ** <journal page X>
|
| - ** <modify page X>
|
| - ** SAVEPOINT sp;
|
| - ** <shrink database file to Y pages>
|
| - ** pagerStress(page X)
|
| - ** ROLLBACK TO sp;
|
| - **
|
| - ** If (X>Y), then when pagerStress is called page X will not be written
|
| - ** out to the database file, but will be dropped from the cache. Then,
|
| - ** following the "ROLLBACK TO sp" statement, reading page X will read
|
| - ** data from the database file. This will be the copy of page X as it
|
| - ** was when the transaction started, not as it was when "SAVEPOINT sp"
|
| - ** was executed.
|
| - **
|
| - ** The solution is to write the current data for page X into the
|
| - ** sub-journal file now (if it is not already there), so that it will
|
| - ** be restored to its current value when the "ROLLBACK TO sp" is
|
| - ** executed.
|
| - */
|
| - if( NEVER(
|
| - rc==SQLITE_OK && pPg->pgno>pPager->dbSize && subjRequiresPage(pPg)
|
| - ) ){
|
| - rc = subjournalPage(pPg);
|
| - }
|
| -
|
| /* Write the contents of the page out to the database file. */
|
| if( rc==SQLITE_OK ){
|
| assert( (pPg->flags&PGHDR_NEED_SYNC)==0 );
|
| @@ -4480,6 +4482,25 @@ static int pagerStress(void *p, PgHdr *pPg){
|
| return pager_error(pPager, rc);
|
| }
|
|
|
| +/*
|
| +** Flush all unreferenced dirty pages to disk.
|
| +*/
|
| +int sqlite3PagerFlush(Pager *pPager){
|
| + int rc = pPager->errCode;
|
| + if( !MEMDB ){
|
| + PgHdr *pList = sqlite3PcacheDirtyList(pPager->pPCache);
|
| + assert( assert_pager_state(pPager) );
|
| + while( rc==SQLITE_OK && pList ){
|
| + PgHdr *pNext = pList->pDirty;
|
| + if( pList->nRef==0 ){
|
| + rc = pagerStress((void*)pPager, pList);
|
| + }
|
| + pList = pNext;
|
| + }
|
| + }
|
| +
|
| + return rc;
|
| +}
|
|
|
| /*
|
| ** Allocate and initialize a new Pager object and put a pointer to it
|
| @@ -4719,7 +4740,7 @@ int sqlite3PagerOpen(
|
| act_like_temp_file:
|
| tempFile = 1;
|
| pPager->eState = PAGER_READER; /* Pretend we already have a lock */
|
| - pPager->eLock = EXCLUSIVE_LOCK; /* Pretend we are in EXCLUSIVE locking mode */
|
| + pPager->eLock = EXCLUSIVE_LOCK; /* Pretend we are in EXCLUSIVE mode */
|
| pPager->noLock = 1; /* Do no locking */
|
| readOnly = (vfsFlags&SQLITE_OPEN_READONLY);
|
| }
|
| @@ -4738,7 +4759,7 @@ act_like_temp_file:
|
| assert( nExtra<1000 );
|
| nExtra = ROUND8(nExtra);
|
| rc = sqlite3PcacheOpen(szPageDflt, nExtra, !memDb,
|
| - !memDb?pagerStress:0, (void *)pPager, pPager->pPCache);
|
| + !memDb?pagerStress:0, (void *)pPager, pPager->pPCache);
|
| }
|
|
|
| /* If an error occurred above, free the Pager structure and close the file.
|
| @@ -4957,7 +4978,7 @@ static int hasHotJournal(Pager *pPager, int *pExists){
|
|
|
| /*
|
| ** This function is called to obtain a shared lock on the database file.
|
| -** It is illegal to call sqlite3PagerAcquire() until after this function
|
| +** It is illegal to call sqlite3PagerGet() until after this function
|
| ** has been successfully called. If a shared-lock is already held when
|
| ** this function is called, it is a no-op.
|
| **
|
| @@ -5118,18 +5139,14 @@ int sqlite3PagerSharedLock(Pager *pPager){
|
| );
|
| }
|
|
|
| - if( !pPager->tempFile && (
|
| - pPager->pBackup
|
| - || sqlite3PcachePagecount(pPager->pPCache)>0
|
| - || USEFETCH(pPager)
|
| - )){
|
| - /* The shared-lock has just been acquired on the database file
|
| - ** and there are already pages in the cache (from a previous
|
| - ** read or write transaction). Check to see if the database
|
| - ** has been modified. If the database has changed, flush the
|
| - ** cache.
|
| + if( !pPager->tempFile && pPager->hasHeldSharedLock ){
|
| + /* The shared-lock has just been acquired then check to
|
| + ** see if the database has been modified. If the database has changed,
|
| + ** flush the cache. The hasHeldSharedLock flag prevents this from
|
| + ** occurring on the very first access to a file, in order to save a
|
| + ** single unnecessary sqlite3OsRead() call at the start-up.
|
| **
|
| - ** Database changes is detected by looking at 15 bytes beginning
|
| + ** Database changes are detected by looking at 15 bytes beginning
|
| ** at offset 24 into the file. The first 4 of these 16 bytes are
|
| ** a 32-bit counter that is incremented with each change. The
|
| ** other bytes change randomly with each file change when
|
| @@ -5195,6 +5212,7 @@ int sqlite3PagerSharedLock(Pager *pPager){
|
| assert( pPager->eState==PAGER_OPEN );
|
| }else{
|
| pPager->eState = PAGER_READER;
|
| + pPager->hasHeldSharedLock = 1;
|
| }
|
| return rc;
|
| }
|
| @@ -5263,7 +5281,7 @@ static void pagerUnlockIfUnused(Pager *pPager){
|
| ** Since Lookup() never goes to disk, it never has to deal with locks
|
| ** or journal files.
|
| */
|
| -int sqlite3PagerAcquire(
|
| +int sqlite3PagerGet(
|
| Pager *pPager, /* The pager open on the database file */
|
| Pgno pgno, /* Page number to fetch */
|
| DbPage **ppPage, /* Write a pointer to the page here */
|
| @@ -5278,20 +5296,25 @@ int sqlite3PagerAcquire(
|
| ** page 1 if there is no write-transaction open or the ACQUIRE_READONLY
|
| ** flag was specified by the caller. And so long as the db is not a
|
| ** temporary or in-memory database. */
|
| - const int bMmapOk = (pgno!=1 && USEFETCH(pPager)
|
| + const int bMmapOk = (pgno>1 && USEFETCH(pPager)
|
| && (pPager->eState==PAGER_READER || (flags & PAGER_GET_READONLY))
|
| #ifdef SQLITE_HAS_CODEC
|
| && pPager->xCodec==0
|
| #endif
|
| );
|
|
|
| + /* Optimization note: Adding the "pgno<=1" term before "pgno==0" here
|
| + ** allows the compiler optimizer to reuse the results of the "pgno>1"
|
| + ** test in the previous statement, and avoid testing pgno==0 in the
|
| + ** common case where pgno is large. */
|
| + if( pgno<=1 && pgno==0 ){
|
| + return SQLITE_CORRUPT_BKPT;
|
| + }
|
| assert( pPager->eState>=PAGER_READER );
|
| assert( assert_pager_state(pPager) );
|
| assert( noContent==0 || bMmapOk==0 );
|
|
|
| - if( pgno==0 ){
|
| - return SQLITE_CORRUPT_BKPT;
|
| - }
|
| + assert( pPager->hasHeldSharedLock==1 );
|
|
|
| /* If the pager is in the error state, return an error immediately.
|
| ** Otherwise, request the page from the PCache layer. */
|
| @@ -5336,9 +5359,14 @@ int sqlite3PagerAcquire(
|
| if( pBase==0 ){
|
| rc = sqlite3PcacheFetchStress(pPager->pPCache, pgno, &pBase);
|
| if( rc!=SQLITE_OK ) goto pager_acquire_err;
|
| + if( pBase==0 ){
|
| + pPg = *ppPage = 0;
|
| + rc = SQLITE_NOMEM;
|
| + goto pager_acquire_err;
|
| + }
|
| }
|
| pPg = *ppPage = sqlite3PcacheFetchFinish(pPager->pPCache, pgno, pBase);
|
| - if( pPg==0 ) rc = SQLITE_NOMEM;
|
| + assert( pPg!=0 );
|
| }
|
| }
|
|
|
| @@ -5349,10 +5377,11 @@ int sqlite3PagerAcquire(
|
| pPg = 0;
|
| goto pager_acquire_err;
|
| }
|
| - assert( (*ppPage)->pgno==pgno );
|
| - assert( (*ppPage)->pPager==pPager || (*ppPage)->pPager==0 );
|
| + assert( pPg==(*ppPage) );
|
| + assert( pPg->pgno==pgno );
|
| + assert( pPg->pPager==pPager || pPg->pPager==0 );
|
|
|
| - if( (*ppPage)->pPager && !noContent ){
|
| + if( pPg->pPager && !noContent ){
|
| /* In this case the pcache already contains an initialized copy of
|
| ** the page. Return without further ado. */
|
| assert( pgno<=PAGER_MAX_PGNO && pgno!=PAGER_MJ_PGNO(pPager) );
|
| @@ -5363,7 +5392,6 @@ int sqlite3PagerAcquire(
|
| /* The pager cache has created a new page. Its content needs to
|
| ** be initialized. */
|
|
|
| - pPg = *ppPage;
|
| pPg->pPager = pPager;
|
|
|
| /* The maximum page number is 2^31. Return SQLITE_CORRUPT if a page
|
| @@ -5441,6 +5469,8 @@ DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno){
|
| assert( pgno!=0 );
|
| assert( pPager->pPCache!=0 );
|
| pPage = sqlite3PcacheFetch(pPager->pPCache, pgno, 0);
|
| + assert( pPage==0 || pPager->hasHeldSharedLock );
|
| + if( pPage==0 ) return 0;
|
| return sqlite3PcacheFetchFinish(pPager->pPCache, pgno, pPage);
|
| }
|
|
|
| @@ -5467,20 +5497,6 @@ void sqlite3PagerUnref(DbPage *pPg){
|
| if( pPg ) sqlite3PagerUnrefNotNull(pPg);
|
| }
|
|
|
| -#if defined(__APPLE__)
|
| -/*
|
| -** Create and return a CFURLRef given a cstring containing the path to a file.
|
| -*/
|
| -static CFURLRef create_cfurl_from_cstring(const char* filePath){
|
| - CFStringRef urlString = CFStringCreateWithFileSystemRepresentation(
|
| - kCFAllocatorDefault, filePath);
|
| - CFURLRef urlRef = CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
|
| - urlString, kCFURLPOSIXPathStyle, FALSE);
|
| - CFRelease(urlString);
|
| - return urlRef;
|
| -}
|
| -#endif
|
| -
|
| /*
|
| ** This function is called at the start of every write transaction.
|
| ** There must already be a RESERVED or EXCLUSIVE lock on the database
|
| @@ -5545,24 +5561,6 @@ static int pager_open_journal(Pager *pPager){
|
| #else
|
| rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, flags, 0);
|
| #endif
|
| -#if defined(__APPLE__)
|
| - /* Set the TimeMachine exclusion metadata for the journal if it has
|
| - ** been set for the database. Only do this for unix-type vfs
|
| - ** implementations. */
|
| - if( rc==SQLITE_OK && pPager->zFilename!=NULL
|
| - && strlen(pPager->zFilename)>0
|
| - && strncmp(pVfs->zName, "unix", 4)==0
|
| - && ( pVfs->zName[4]=='-' || pVfs->zName[4]=='\0' ) ){
|
| - CFURLRef database = create_cfurl_from_cstring(pPager->zFilename);
|
| - if( CSBackupIsItemExcluded(database, NULL) ){
|
| - CFURLRef journal = create_cfurl_from_cstring(pPager->zJournal);
|
| - /* Ignore errors from the following exclusion call. */
|
| - CSBackupSetItemExcluded(journal, TRUE, FALSE);
|
| - CFRelease(journal);
|
| - }
|
| - CFRelease(database);
|
| - }
|
| -#endif
|
| }
|
| }
|
| assert( rc!=SQLITE_OK || isOpen(pPager->jfd) );
|
| @@ -5629,7 +5627,7 @@ int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory){
|
| if( rc!=SQLITE_OK ){
|
| return rc;
|
| }
|
| - sqlite3WalExclusiveMode(pPager->pWal, 1);
|
| + (void)sqlite3WalExclusiveMode(pPager->pWal, 1);
|
| }
|
|
|
| /* Grab the write lock on the log file. If successful, upgrade to
|
| @@ -5677,6 +5675,59 @@ int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory){
|
| }
|
|
|
| /*
|
| +** Write page pPg onto the end of the rollback journal.
|
| +*/
|
| +static SQLITE_NOINLINE int pagerAddPageToRollbackJournal(PgHdr *pPg){
|
| + Pager *pPager = pPg->pPager;
|
| + int rc;
|
| + u32 cksum;
|
| + char *pData2;
|
| + i64 iOff = pPager->journalOff;
|
| +
|
| + /* We should never write to the journal file the page that
|
| + ** contains the database locks. The following assert verifies
|
| + ** that we do not. */
|
| + assert( pPg->pgno!=PAGER_MJ_PGNO(pPager) );
|
| +
|
| + assert( pPager->journalHdr<=pPager->journalOff );
|
| + CODEC2(pPager, pPg->pData, pPg->pgno, 7, return SQLITE_NOMEM, pData2);
|
| + cksum = pager_cksum(pPager, (u8*)pData2);
|
| +
|
| + /* Even if an IO or diskfull error occurs while journalling the
|
| + ** page in the block above, set the need-sync flag for the page.
|
| + ** Otherwise, when the transaction is rolled back, the logic in
|
| + ** playback_one_page() will think that the page needs to be restored
|
| + ** in the database file. And if an IO error occurs while doing so,
|
| + ** then corruption may follow.
|
| + */
|
| + pPg->flags |= PGHDR_NEED_SYNC;
|
| +
|
| + rc = write32bits(pPager->jfd, iOff, pPg->pgno);
|
| + if( rc!=SQLITE_OK ) return rc;
|
| + rc = sqlite3OsWrite(pPager->jfd, pData2, pPager->pageSize, iOff+4);
|
| + if( rc!=SQLITE_OK ) return rc;
|
| + rc = write32bits(pPager->jfd, iOff+pPager->pageSize+4, cksum);
|
| + if( rc!=SQLITE_OK ) return rc;
|
| +
|
| + IOTRACE(("JOUT %p %d %lld %d\n", pPager, pPg->pgno,
|
| + pPager->journalOff, pPager->pageSize));
|
| + PAGER_INCR(sqlite3_pager_writej_count);
|
| + PAGERTRACE(("JOURNAL %d page %d needSync=%d hash(%08x)\n",
|
| + PAGERID(pPager), pPg->pgno,
|
| + ((pPg->flags&PGHDR_NEED_SYNC)?1:0), pager_pagehash(pPg)));
|
| +
|
| + pPager->journalOff += 8 + pPager->pageSize;
|
| + pPager->nRec++;
|
| + assert( pPager->pInJournal!=0 );
|
| + rc = sqlite3BitvecSet(pPager->pInJournal, pPg->pgno);
|
| + testcase( rc==SQLITE_NOMEM );
|
| + assert( rc==SQLITE_OK || rc==SQLITE_NOMEM );
|
| + rc |= addToSavepointBitvecs(pPager, pPg->pgno);
|
| + assert( rc==SQLITE_OK || rc==SQLITE_NOMEM );
|
| + return rc;
|
| +}
|
| +
|
| +/*
|
| ** Mark a single data page as writeable. The page is written into the
|
| ** main journal or sub-journal as required. If the page is written into
|
| ** one of the journals, the corresponding bit is set in the
|
| @@ -5686,7 +5737,6 @@ int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory){
|
| static int pager_write(PgHdr *pPg){
|
| Pager *pPager = pPg->pPager;
|
| int rc = SQLITE_OK;
|
| - int inJournal;
|
|
|
| /* 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.
|
| @@ -5699,7 +5749,6 @@ static int pager_write(PgHdr *pPg){
|
| assert( assert_pager_state(pPager) );
|
| assert( pPager->errCode==0 );
|
| assert( pPager->readOnly==0 );
|
| -
|
| CHECK_PAGE(pPg);
|
|
|
| /* The journal file needs to be opened. Higher level routines have already
|
| @@ -5718,91 +5767,48 @@ static int pager_write(PgHdr *pPg){
|
| assert( pPager->eState>=PAGER_WRITER_CACHEMOD );
|
| assert( assert_pager_state(pPager) );
|
|
|
| - /* Mark the page as dirty. If the page has already been written
|
| - ** to the journal then we can return right away.
|
| - */
|
| + /* Mark the page that is about to be modified as dirty. */
|
| sqlite3PcacheMakeDirty(pPg);
|
| - inJournal = pageInJournal(pPager, pPg);
|
| - if( inJournal && (pPager->nSavepoint==0 || !subjRequiresPage(pPg)) ){
|
| - assert( !pagerUseWal(pPager) );
|
| - }else{
|
| -
|
| - /* The transaction journal now exists and we have a RESERVED or an
|
| - ** EXCLUSIVE lock on the main database file. Write the current page to
|
| - ** the transaction journal if it is not there already.
|
| - */
|
| - if( !inJournal && !pagerUseWal(pPager) ){
|
| - assert( pagerUseWal(pPager)==0 );
|
| - if( pPg->pgno<=pPager->dbOrigSize && isOpen(pPager->jfd) ){
|
| - u32 cksum;
|
| - char *pData2;
|
| - i64 iOff = pPager->journalOff;
|
| -
|
| - /* We should never write to the journal file the page that
|
| - ** contains the database locks. The following assert verifies
|
| - ** that we do not. */
|
| - assert( pPg->pgno!=PAGER_MJ_PGNO(pPager) );
|
| -
|
| - assert( pPager->journalHdr<=pPager->journalOff );
|
| - CODEC2(pPager, pPg->pData, pPg->pgno, 7, return SQLITE_NOMEM, pData2);
|
| - cksum = pager_cksum(pPager, (u8*)pData2);
|
| -
|
| - /* Even if an IO or diskfull error occurs while journalling the
|
| - ** page in the block above, set the need-sync flag for the page.
|
| - ** Otherwise, when the transaction is rolled back, the logic in
|
| - ** playback_one_page() will think that the page needs to be restored
|
| - ** in the database file. And if an IO error occurs while doing so,
|
| - ** then corruption may follow.
|
| - */
|
| - pPg->flags |= PGHDR_NEED_SYNC;
|
| -
|
| - rc = write32bits(pPager->jfd, iOff, pPg->pgno);
|
| - if( rc!=SQLITE_OK ) return rc;
|
| - rc = sqlite3OsWrite(pPager->jfd, pData2, pPager->pageSize, iOff+4);
|
| - if( rc!=SQLITE_OK ) return rc;
|
| - rc = write32bits(pPager->jfd, iOff+pPager->pageSize+4, cksum);
|
| - if( rc!=SQLITE_OK ) return rc;
|
|
|
| - IOTRACE(("JOUT %p %d %lld %d\n", pPager, pPg->pgno,
|
| - pPager->journalOff, pPager->pageSize));
|
| - PAGER_INCR(sqlite3_pager_writej_count);
|
| - PAGERTRACE(("JOURNAL %d page %d needSync=%d hash(%08x)\n",
|
| - PAGERID(pPager), pPg->pgno,
|
| - ((pPg->flags&PGHDR_NEED_SYNC)?1:0), pager_pagehash(pPg)));
|
| -
|
| - pPager->journalOff += 8 + pPager->pageSize;
|
| - pPager->nRec++;
|
| - assert( pPager->pInJournal!=0 );
|
| - rc = sqlite3BitvecSet(pPager->pInJournal, pPg->pgno);
|
| - testcase( rc==SQLITE_NOMEM );
|
| - assert( rc==SQLITE_OK || rc==SQLITE_NOMEM );
|
| - rc |= addToSavepointBitvecs(pPager, pPg->pgno);
|
| - if( rc!=SQLITE_OK ){
|
| - assert( rc==SQLITE_NOMEM );
|
| - return rc;
|
| - }
|
| - }else{
|
| - if( pPager->eState!=PAGER_WRITER_DBMOD ){
|
| - pPg->flags |= PGHDR_NEED_SYNC;
|
| - }
|
| - PAGERTRACE(("APPEND %d page %d needSync=%d\n",
|
| - PAGERID(pPager), pPg->pgno,
|
| - ((pPg->flags&PGHDR_NEED_SYNC)?1:0)));
|
| + /* If a rollback journal is in use, them make sure the page that is about
|
| + ** to change is in the rollback journal, or if the page is a new page off
|
| + ** then end of the file, make sure it is marked as PGHDR_NEED_SYNC.
|
| + */
|
| + assert( (pPager->pInJournal!=0) == isOpen(pPager->jfd) );
|
| + if( pPager->pInJournal!=0
|
| + && sqlite3BitvecTestNotNull(pPager->pInJournal, pPg->pgno)==0
|
| + ){
|
| + assert( pagerUseWal(pPager)==0 );
|
| + if( pPg->pgno<=pPager->dbOrigSize ){
|
| + rc = pagerAddPageToRollbackJournal(pPg);
|
| + if( rc!=SQLITE_OK ){
|
| + return rc;
|
| }
|
| - }
|
| -
|
| - /* If the statement journal is open and the page is not in it,
|
| - ** then write the current page to the statement journal. Note that
|
| - ** the statement journal format differs from the standard journal format
|
| - ** in that it omits the checksums and the header.
|
| - */
|
| - if( pPager->nSavepoint>0 && subjRequiresPage(pPg) ){
|
| - rc = subjournalPage(pPg);
|
| + }else{
|
| + if( pPager->eState!=PAGER_WRITER_DBMOD ){
|
| + pPg->flags |= PGHDR_NEED_SYNC;
|
| + }
|
| + PAGERTRACE(("APPEND %d page %d needSync=%d\n",
|
| + PAGERID(pPager), pPg->pgno,
|
| + ((pPg->flags&PGHDR_NEED_SYNC)?1:0)));
|
| }
|
| }
|
|
|
| - /* Update the database size and return.
|
| + /* The PGHDR_DIRTY bit is set above when the page was added to the dirty-list
|
| + ** and before writing the page into the rollback journal. Wait until now,
|
| + ** after the page has been successfully journalled, before setting the
|
| + ** PGHDR_WRITEABLE bit that indicates that the page can be safely modified.
|
| + */
|
| + pPg->flags |= PGHDR_WRITEABLE;
|
| +
|
| + /* If the statement journal is open and the page is not in it,
|
| + ** then write the page into the statement journal.
|
| */
|
| + if( pPager->nSavepoint>0 ){
|
| + rc = subjournalPageIfRequired(pPg);
|
| + }
|
| +
|
| + /* Update the database size and return. */
|
| if( pPager->dbSize<pPg->pgno ){
|
| pPager->dbSize = pPg->pgno;
|
| }
|
| @@ -5817,17 +5823,17 @@ static int pager_write(PgHdr *pPg){
|
| ** a write.
|
| **
|
| ** Usually, the sector size is less than or equal to the page size, in which
|
| -** case pages can be individually written. This routine only runs in the exceptional
|
| -** case where the page size is smaller than the sector size.
|
| +** case pages can be individually written. This routine only runs in the
|
| +** exceptional case where the page size is smaller than the sector size.
|
| */
|
| static SQLITE_NOINLINE int pagerWriteLargeSector(PgHdr *pPg){
|
| - int rc = SQLITE_OK; /* Return code */
|
| - Pgno nPageCount; /* Total number of pages in database file */
|
| - Pgno pg1; /* First page of the sector pPg is located on. */
|
| - int nPage = 0; /* Number of pages starting at pg1 to journal */
|
| - int ii; /* Loop counter */
|
| - int needSync = 0; /* True if any page has PGHDR_NEED_SYNC */
|
| - Pager *pPager = pPg->pPager; /* The pager that owns pPg */
|
| + int rc = SQLITE_OK; /* Return code */
|
| + Pgno nPageCount; /* Total number of pages in database file */
|
| + Pgno pg1; /* First page of the sector pPg is located on. */
|
| + int nPage = 0; /* Number of pages starting at pg1 to journal */
|
| + int ii; /* Loop counter */
|
| + int needSync = 0; /* True if any page has PGHDR_NEED_SYNC */
|
| + Pager *pPager = pPg->pPager; /* The pager that owns pPg */
|
| Pgno nPagePerSector = (pPager->sectorSize/pPager->pageSize);
|
|
|
| /* Set the doNotSpill NOSYNC bit to 1. This is because we cannot allow
|
| @@ -5861,7 +5867,7 @@ static SQLITE_NOINLINE int pagerWriteLargeSector(PgHdr *pPg){
|
| PgHdr *pPage;
|
| if( pg==pPg->pgno || !sqlite3BitvecTest(pPager->pInJournal, pg) ){
|
| if( pg!=PAGER_MJ_PGNO(pPager) ){
|
| - rc = sqlite3PagerGet(pPager, pg, &pPage);
|
| + rc = sqlite3PagerGet(pPager, pg, &pPage, 0);
|
| if( rc==SQLITE_OK ){
|
| rc = pager_write(pPage);
|
| if( pPage->flags&PGHDR_NEED_SYNC ){
|
| @@ -5915,11 +5921,16 @@ static SQLITE_NOINLINE int pagerWriteLargeSector(PgHdr *pPg){
|
| ** as appropriate. Otherwise, SQLITE_OK.
|
| */
|
| int sqlite3PagerWrite(PgHdr *pPg){
|
| + Pager *pPager = pPg->pPager;
|
| assert( (pPg->flags & PGHDR_MMAP)==0 );
|
| - assert( pPg->pPager->eState>=PAGER_WRITER_LOCKED );
|
| - assert( pPg->pPager->eState!=PAGER_ERROR );
|
| - assert( assert_pager_state(pPg->pPager) );
|
| - if( pPg->pPager->sectorSize > (u32)pPg->pPager->pageSize ){
|
| + assert( pPager->eState>=PAGER_WRITER_LOCKED );
|
| + assert( assert_pager_state(pPager) );
|
| + if( pPager->errCode ){
|
| + return pPager->errCode;
|
| + }else if( (pPg->flags & PGHDR_WRITEABLE)!=0 && pPager->dbSize>=pPg->pgno ){
|
| + if( pPager->nSavepoint ) return subjournalPageIfRequired(pPg);
|
| + return SQLITE_OK;
|
| + }else if( pPager->sectorSize > (u32)pPager->pageSize ){
|
| return pagerWriteLargeSector(pPg);
|
| }else{
|
| return pager_write(pPg);
|
| @@ -5933,7 +5944,7 @@ int sqlite3PagerWrite(PgHdr *pPg){
|
| */
|
| #ifndef NDEBUG
|
| int sqlite3PagerIswriteable(DbPage *pPg){
|
| - return pPg->flags&PGHDR_DIRTY;
|
| + return pPg->flags & PGHDR_WRITEABLE;
|
| }
|
| #endif
|
|
|
| @@ -5957,6 +5968,7 @@ void sqlite3PagerDontWrite(PgHdr *pPg){
|
| PAGERTRACE(("DONT_WRITE page %d of %d\n", pPg->pgno, PAGERID(pPager)));
|
| IOTRACE(("CLEAN %p %d\n", pPager, pPg->pgno))
|
| pPg->flags |= PGHDR_DONT_WRITE;
|
| + pPg->flags &= ~PGHDR_WRITEABLE;
|
| pager_set_pagehash(pPg);
|
| }
|
| }
|
| @@ -6015,7 +6027,7 @@ static int pager_incr_changecounter(Pager *pPager, int isDirectMode){
|
| assert( !pPager->tempFile && isOpen(pPager->fd) );
|
|
|
| /* Open page 1 of the file for writing. */
|
| - rc = sqlite3PagerGet(pPager, 1, &pPgHdr);
|
| + rc = sqlite3PagerGet(pPager, 1, &pPgHdr, 0);
|
| assert( pPgHdr==0 || rc==SQLITE_OK );
|
|
|
| /* If page one was fetched successfully, and this function is not
|
| @@ -6093,14 +6105,17 @@ int sqlite3PagerSync(Pager *pPager, const char *zMaster){
|
| ** returned.
|
| */
|
| int sqlite3PagerExclusiveLock(Pager *pPager){
|
| - int rc = SQLITE_OK;
|
| - assert( pPager->eState==PAGER_WRITER_CACHEMOD
|
| - || pPager->eState==PAGER_WRITER_DBMOD
|
| - || pPager->eState==PAGER_WRITER_LOCKED
|
| - );
|
| + int rc = pPager->errCode;
|
| assert( assert_pager_state(pPager) );
|
| - if( 0==pagerUseWal(pPager) ){
|
| - rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
|
| + if( rc==SQLITE_OK ){
|
| + assert( pPager->eState==PAGER_WRITER_CACHEMOD
|
| + || pPager->eState==PAGER_WRITER_DBMOD
|
| + || pPager->eState==PAGER_WRITER_LOCKED
|
| + );
|
| + assert( assert_pager_state(pPager) );
|
| + if( 0==pagerUseWal(pPager) ){
|
| + rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
|
| + }
|
| }
|
| return rc;
|
| }
|
| @@ -6167,7 +6182,7 @@ int sqlite3PagerCommitPhaseOne(
|
| if( pList==0 ){
|
| /* Must have at least one page for the WAL commit flag.
|
| ** Ticket [2d1a5c67dfc2363e44f29d9bbd57f] 2011-05-18 */
|
| - rc = sqlite3PagerGet(pPager, 1, &pPageOne);
|
| + rc = sqlite3PagerGet(pPager, 1, &pPageOne, 0);
|
| pList = pPageOne;
|
| pList->pDirty = 0;
|
| }
|
| @@ -6339,6 +6354,7 @@ int sqlite3PagerCommitPhaseTwo(Pager *pPager){
|
| }
|
|
|
| PAGERTRACE(("COMMIT %d\n", PAGERID(pPager)));
|
| + pPager->iDataVersion++;
|
| rc = pager_end_transaction(pPager, pPager->setMaster, 1);
|
| return pager_error(pPager, rc);
|
| }
|
| @@ -6422,12 +6438,14 @@ u8 sqlite3PagerIsreadonly(Pager *pPager){
|
| return pPager->readOnly;
|
| }
|
|
|
| +#ifdef SQLITE_DEBUG
|
| /*
|
| -** Return the number of references to the pager.
|
| +** Return the sum of the reference counts for all pages held by pPager.
|
| */
|
| int sqlite3PagerRefcount(Pager *pPager){
|
| return sqlite3PcacheRefCount(pPager->pPCache);
|
| }
|
| +#endif
|
|
|
| /*
|
| ** Return the approximate number of bytes of memory currently
|
| @@ -6510,54 +6528,62 @@ int sqlite3PagerIsMemdb(Pager *pPager){
|
| ** occurs while opening the sub-journal file, then an IO error code is
|
| ** returned. Otherwise, SQLITE_OK.
|
| */
|
| -int sqlite3PagerOpenSavepoint(Pager *pPager, int nSavepoint){
|
| +static SQLITE_NOINLINE int pagerOpenSavepoint(Pager *pPager, int nSavepoint){
|
| int rc = SQLITE_OK; /* Return code */
|
| int nCurrent = pPager->nSavepoint; /* Current number of savepoints */
|
| + int ii; /* Iterator variable */
|
| + PagerSavepoint *aNew; /* New Pager.aSavepoint array */
|
|
|
| assert( pPager->eState>=PAGER_WRITER_LOCKED );
|
| assert( assert_pager_state(pPager) );
|
| + assert( nSavepoint>nCurrent && pPager->useJournal );
|
|
|
| - if( nSavepoint>nCurrent && pPager->useJournal ){
|
| - int ii; /* Iterator variable */
|
| - PagerSavepoint *aNew; /* New Pager.aSavepoint array */
|
| + /* Grow the Pager.aSavepoint array using realloc(). Return SQLITE_NOMEM
|
| + ** if the allocation fails. Otherwise, zero the new portion in case a
|
| + ** malloc failure occurs while populating it in the for(...) loop below.
|
| + */
|
| + aNew = (PagerSavepoint *)sqlite3Realloc(
|
| + pPager->aSavepoint, sizeof(PagerSavepoint)*nSavepoint
|
| + );
|
| + if( !aNew ){
|
| + return SQLITE_NOMEM;
|
| + }
|
| + memset(&aNew[nCurrent], 0, (nSavepoint-nCurrent) * sizeof(PagerSavepoint));
|
| + pPager->aSavepoint = aNew;
|
|
|
| - /* Grow the Pager.aSavepoint array using realloc(). Return SQLITE_NOMEM
|
| - ** if the allocation fails. Otherwise, zero the new portion in case a
|
| - ** malloc failure occurs while populating it in the for(...) loop below.
|
| - */
|
| - aNew = (PagerSavepoint *)sqlite3Realloc(
|
| - pPager->aSavepoint, sizeof(PagerSavepoint)*nSavepoint
|
| - );
|
| - if( !aNew ){
|
| + /* Populate the PagerSavepoint structures just allocated. */
|
| + for(ii=nCurrent; ii<nSavepoint; ii++){
|
| + aNew[ii].nOrig = pPager->dbSize;
|
| + if( isOpen(pPager->jfd) && pPager->journalOff>0 ){
|
| + aNew[ii].iOffset = pPager->journalOff;
|
| + }else{
|
| + aNew[ii].iOffset = JOURNAL_HDR_SZ(pPager);
|
| + }
|
| + aNew[ii].iSubRec = pPager->nSubRec;
|
| + aNew[ii].pInSavepoint = sqlite3BitvecCreate(pPager->dbSize);
|
| + if( !aNew[ii].pInSavepoint ){
|
| return SQLITE_NOMEM;
|
| }
|
| - memset(&aNew[nCurrent], 0, (nSavepoint-nCurrent) * sizeof(PagerSavepoint));
|
| - pPager->aSavepoint = aNew;
|
| -
|
| - /* Populate the PagerSavepoint structures just allocated. */
|
| - for(ii=nCurrent; ii<nSavepoint; ii++){
|
| - aNew[ii].nOrig = pPager->dbSize;
|
| - if( isOpen(pPager->jfd) && pPager->journalOff>0 ){
|
| - aNew[ii].iOffset = pPager->journalOff;
|
| - }else{
|
| - aNew[ii].iOffset = JOURNAL_HDR_SZ(pPager);
|
| - }
|
| - aNew[ii].iSubRec = pPager->nSubRec;
|
| - aNew[ii].pInSavepoint = sqlite3BitvecCreate(pPager->dbSize);
|
| - if( !aNew[ii].pInSavepoint ){
|
| - return SQLITE_NOMEM;
|
| - }
|
| - if( pagerUseWal(pPager) ){
|
| - sqlite3WalSavepoint(pPager->pWal, aNew[ii].aWalData);
|
| - }
|
| - pPager->nSavepoint = ii+1;
|
| + if( pagerUseWal(pPager) ){
|
| + sqlite3WalSavepoint(pPager->pWal, aNew[ii].aWalData);
|
| }
|
| - assert( pPager->nSavepoint==nSavepoint );
|
| - assertTruncateConstraint(pPager);
|
| + pPager->nSavepoint = ii+1;
|
| }
|
| -
|
| + assert( pPager->nSavepoint==nSavepoint );
|
| + assertTruncateConstraint(pPager);
|
| return rc;
|
| }
|
| +int sqlite3PagerOpenSavepoint(Pager *pPager, int nSavepoint){
|
| + assert( pPager->eState>=PAGER_WRITER_LOCKED );
|
| + assert( assert_pager_state(pPager) );
|
| +
|
| + if( nSavepoint>pPager->nSavepoint && pPager->useJournal ){
|
| + return pagerOpenSavepoint(pPager, nSavepoint);
|
| + }else{
|
| + return SQLITE_OK;
|
| + }
|
| +}
|
| +
|
|
|
| /*
|
| ** This function is called to rollback or release (commit) a savepoint.
|
| @@ -6653,7 +6679,7 @@ const char *sqlite3PagerFilename(Pager *pPager, int nullIfMemDb){
|
| /*
|
| ** Return the VFS structure for the pager.
|
| */
|
| -const sqlite3_vfs *sqlite3PagerVfs(Pager *pPager){
|
| +sqlite3_vfs *sqlite3PagerVfs(Pager *pPager){
|
| return pPager->pVfs;
|
| }
|
|
|
| @@ -6667,6 +6693,18 @@ sqlite3_file *sqlite3PagerFile(Pager *pPager){
|
| }
|
|
|
| /*
|
| +** Return the file handle for the journal file (if it exists).
|
| +** This will be either the rollback journal or the WAL file.
|
| +*/
|
| +sqlite3_file *sqlite3PagerJrnlFile(Pager *pPager){
|
| +#if SQLITE_OMIT_WAL
|
| + return pPager->jfd;
|
| +#else
|
| + return pPager->pWal ? sqlite3WalFile(pPager->pWal) : pPager->jfd;
|
| +#endif
|
| +}
|
| +
|
| +/*
|
| ** Return the full pathname of the journal file.
|
| */
|
| const char *sqlite3PagerJournalname(Pager *pPager){
|
| @@ -6788,9 +6826,8 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){
|
| ** one or more savepoint bitvecs. This is the reason this function
|
| ** may return SQLITE_NOMEM.
|
| */
|
| - if( pPg->flags&PGHDR_DIRTY
|
| - && subjRequiresPage(pPg)
|
| - && SQLITE_OK!=(rc = subjournalPage(pPg))
|
| + if( (pPg->flags & PGHDR_DIRTY)!=0
|
| + && SQLITE_OK!=(rc = subjournalPageIfRequired(pPg))
|
| ){
|
| return rc;
|
| }
|
| @@ -6862,7 +6899,7 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){
|
| ** the journal file twice, but that is not a problem.
|
| */
|
| PgHdr *pPgHdr;
|
| - rc = sqlite3PagerGet(pPager, needSyncPgno, &pPgHdr);
|
| + rc = sqlite3PagerGet(pPager, needSyncPgno, &pPgHdr, 0);
|
| if( rc!=SQLITE_OK ){
|
| if( needSyncPgno<=pPager->dbOrigSize ){
|
| assert( pPager->pTmpSpace!=0 );
|
| @@ -6880,6 +6917,18 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){
|
| #endif
|
|
|
| /*
|
| +** The page handle passed as the first argument refers to a dirty page
|
| +** with a page number other than iNew. This function changes the page's
|
| +** page number to iNew and sets the value of the PgHdr.flags field to
|
| +** the value passed as the third parameter.
|
| +*/
|
| +void sqlite3PagerRekey(DbPage *pPg, Pgno iNew, u16 flags){
|
| + assert( pPg->pgno!=iNew );
|
| + pPg->flags = flags;
|
| + sqlite3PcacheMove(pPg, iNew);
|
| +}
|
| +
|
| +/*
|
| ** Return a pointer to the data for the specified page.
|
| */
|
| void *sqlite3PagerGetData(DbPage *pPg){
|
| @@ -7024,6 +7073,8 @@ int sqlite3PagerSetJournalMode(Pager *pPager, int eMode){
|
| }
|
| assert( state==pPager->eState );
|
| }
|
| + }else if( eMode==PAGER_JOURNALMODE_OFF ){
|
| + sqlite3OsClose(pPager->jfd);
|
| }
|
| }
|
|
|
| @@ -7095,7 +7146,8 @@ int sqlite3PagerCheckpoint(Pager *pPager, int eMode, int *pnLog, int *pnCkpt){
|
| int rc = SQLITE_OK;
|
| if( pPager->pWal ){
|
| rc = sqlite3WalCheckpoint(pPager->pWal, eMode,
|
| - pPager->xBusyHandler, pPager->pBusyHandlerArg,
|
| + (eMode==SQLITE_CHECKPOINT_PASSIVE ? 0 : pPager->xBusyHandler),
|
| + pPager->pBusyHandlerArg,
|
| pPager->ckptSyncFlags, pPager->pageSize, (u8 *)pPager->pTmpSpace,
|
| pnLog, pnCkpt
|
| );
|
| @@ -7261,6 +7313,34 @@ int sqlite3PagerCloseWal(Pager *pPager){
|
| return rc;
|
| }
|
|
|
| +#ifdef SQLITE_ENABLE_SNAPSHOT
|
| +/*
|
| +** If this is a WAL database, obtain a snapshot handle for the snapshot
|
| +** currently open. Otherwise, return an error.
|
| +*/
|
| +int sqlite3PagerSnapshotGet(Pager *pPager, sqlite3_snapshot **ppSnapshot){
|
| + int rc = SQLITE_ERROR;
|
| + if( pPager->pWal ){
|
| + rc = sqlite3WalSnapshotGet(pPager->pWal, ppSnapshot);
|
| + }
|
| + return rc;
|
| +}
|
| +
|
| +/*
|
| +** If this is a WAL database, store a pointer to pSnapshot. Next time a
|
| +** read transaction is opened, attempt to read from the snapshot it
|
| +** identifies. If this is not a WAL database, return an error.
|
| +*/
|
| +int sqlite3PagerSnapshotOpen(Pager *pPager, sqlite3_snapshot *pSnapshot){
|
| + int rc = SQLITE_OK;
|
| + if( pPager->pWal ){
|
| + sqlite3WalSnapshotOpen(pPager->pWal, pSnapshot);
|
| + }else{
|
| + rc = SQLITE_ERROR;
|
| + }
|
| + return rc;
|
| +}
|
| +#endif /* SQLITE_ENABLE_SNAPSHOT */
|
| #endif /* !SQLITE_OMIT_WAL */
|
|
|
| #ifdef SQLITE_ENABLE_ZIPVFS
|
| @@ -7277,4 +7357,5 @@ int sqlite3PagerWalFramesize(Pager *pPager){
|
| }
|
| #endif
|
|
|
| +
|
| #endif /* SQLITE_OMIT_DISKIO */
|
|
|