Index: third_party/sqlite/src/src/pager.c |
diff --git a/third_party/sqlite/src/src/pager.c b/third_party/sqlite/src/src/pager.c |
index 2c904d2df1f0f5ca08f9566851b5b9a9e319b9de..40c4dd9d88fa9daab5e0d72d97dbc19750a6f1e6 100644 |
--- a/third_party/sqlite/src/src/pager.c |
+++ b/third_party/sqlite/src/src/pager.c |
@@ -428,6 +428,7 @@ int sqlite3PagerTrace=1; /* True to enable tracing */ |
*/ |
#define MAX_SECTOR_SIZE 0x10000 |
+ |
/* |
** An instance of the following structure is allocated for each active |
** savepoint and statement transaction in the system. All such structures |
@@ -623,6 +624,7 @@ struct Pager { |
u8 useJournal; /* Use a rollback journal on this file */ |
u8 noSync; /* Do not sync the journal if true */ |
u8 fullSync; /* Do extra syncs of the journal for robustness */ |
+ u8 extraSync; /* sync directory after journal delete */ |
u8 ckptSyncFlags; /* SYNC_NORMAL or SYNC_FULL for checkpoint */ |
u8 walSyncFlags; /* SYNC_NORMAL or SYNC_FULL for wal writes */ |
u8 syncFlags; /* SYNC_NORMAL or SYNC_FULL otherwise */ |
@@ -691,6 +693,7 @@ struct Pager { |
int nRead; /* Database pages read */ |
#endif |
void (*xReiniter)(DbPage*); /* Call this routine when reloading pages */ |
+ int (*xGet)(Pager*,Pgno,DbPage**,int); /* Routine to fetch a patch */ |
#ifdef SQLITE_HAS_CODEC |
void *(*xCodec)(void*,void*,Pgno,int); /* Routine for en/decoding data */ |
void (*xCodecSizeChng)(void*,int,int); /* Notify of page size changes */ |
@@ -811,13 +814,20 @@ static const unsigned char aJournalMagic[] = { |
#define isOpen(pFd) ((pFd)->pMethods!=0) |
/* |
-** Return true if this pager uses a write-ahead log instead of the usual |
-** rollback journal. Otherwise false. |
+** Return true if this pager uses a write-ahead log to read page pgno. |
+** Return false if the pager reads pgno directly from the database. |
*/ |
-#ifndef SQLITE_OMIT_WAL |
-static int pagerUseWal(Pager *pPager){ |
- return (pPager->pWal!=0); |
+#if !defined(SQLITE_OMIT_WAL) && defined(SQLITE_DIRECT_OVERFLOW_READ) |
+int sqlite3PagerUseWal(Pager *pPager, Pgno pgno){ |
+ u32 iRead = 0; |
+ int rc; |
+ if( pPager->pWal==0 ) return 0; |
+ rc = sqlite3WalFindFrame(pPager->pWal, pgno, &iRead); |
+ return rc || iRead; |
} |
+#endif |
+#ifndef SQLITE_OMIT_WAL |
+# define pagerUseWal(x) ((x)->pWal!=0) |
#else |
# define pagerUseWal(x) 0 |
# define pagerRollbackWal(x) 0 |
@@ -870,6 +880,7 @@ static int assert_pager_state(Pager *p){ |
** state. |
*/ |
if( MEMDB ){ |
+ assert( !isOpen(p->fd) ); |
assert( p->noSync ); |
assert( p->journalMode==PAGER_JOURNALMODE_OFF |
|| p->journalMode==PAGER_JOURNALMODE_MEMORY |
@@ -956,7 +967,7 @@ static int assert_pager_state(Pager *p){ |
** back to OPEN state. |
*/ |
assert( pPager->errCode!=SQLITE_OK ); |
- assert( sqlite3PcacheRefCount(pPager->pPCache)>0 ); |
+ assert( sqlite3PcacheRefCount(pPager->pPCache)>0 || pPager->tempFile ); |
break; |
} |
@@ -1015,6 +1026,33 @@ static char *print_pager_state(Pager *p){ |
} |
#endif |
+/* Forward references to the various page getters */ |
+static int getPageNormal(Pager*,Pgno,DbPage**,int); |
+static int getPageError(Pager*,Pgno,DbPage**,int); |
+#if SQLITE_MAX_MMAP_SIZE>0 |
+static int getPageMMap(Pager*,Pgno,DbPage**,int); |
+#endif |
+ |
+/* |
+** Set the Pager.xGet method for the appropriate routine used to fetch |
+** content from the pager. |
+*/ |
+static void setGetterMethod(Pager *pPager){ |
+ if( pPager->errCode ){ |
+ pPager->xGet = getPageError; |
+#if SQLITE_MAX_MMAP_SIZE>0 |
+ }else if( USEFETCH(pPager) |
+#ifdef SQLITE_HAS_CODEC |
+ && pPager->xCodec==0 |
+#endif |
+ ){ |
+ pPager->xGet = getPageMMap; |
+#endif /* SQLITE_MAX_MMAP_SIZE>0 */ |
+ }else{ |
+ pPager->xGet = getPageNormal; |
+ } |
+} |
+ |
/* |
** Return true if it is necessary to write page *pPg into the sub-journal. |
** A page needs to be written into the sub-journal if there exists one |
@@ -1168,6 +1206,8 @@ static int jrnlBufferSize(Pager *pPager){ |
return JOURNAL_HDR_SZ(pPager) + JOURNAL_PG_SZ(pPager); |
} |
+#else |
+# define jrnlBufferSize(x) 0 |
#endif |
/* |
@@ -1328,6 +1368,7 @@ static i64 journalHdrOffset(Pager *pPager){ |
static int zeroJournalHdr(Pager *pPager, int doTruncate){ |
int rc = SQLITE_OK; /* Return code */ |
assert( isOpen(pPager->jfd) ); |
+ assert( !sqlite3JournalIsInMemory(pPager->jfd) ); |
if( pPager->journalOff ){ |
const i64 iLimit = pPager->journalSizeLimit; /* Local cache of jsl */ |
@@ -1709,7 +1750,7 @@ static void releaseAllSavepoints(Pager *pPager){ |
for(ii=0; ii<pPager->nSavepoint; ii++){ |
sqlite3BitvecDestroy(pPager->aSavepoint[ii].pInSavepoint); |
} |
- if( !pPager->exclusiveMode || sqlite3IsMemJournal(pPager->sjfd) ){ |
+ if( !pPager->exclusiveMode || sqlite3JournalIsInMemory(pPager->sjfd) ){ |
sqlite3OsClose(pPager->sjfd); |
} |
sqlite3_free(pPager->aSavepoint); |
@@ -1815,13 +1856,18 @@ static void pager_unlock(Pager *pPager){ |
** it can safely move back to PAGER_OPEN state. This happens in both |
** normal and exclusive-locking mode. |
*/ |
+ assert( pPager->errCode==SQLITE_OK || !MEMDB ); |
if( pPager->errCode ){ |
- assert( !MEMDB ); |
- pager_reset(pPager); |
- pPager->changeCountDone = pPager->tempFile; |
- pPager->eState = PAGER_OPEN; |
- pPager->errCode = SQLITE_OK; |
+ if( pPager->tempFile==0 ){ |
+ pager_reset(pPager); |
+ pPager->changeCountDone = 0; |
+ pPager->eState = PAGER_OPEN; |
+ }else{ |
+ pPager->eState = (isOpen(pPager->jfd) ? PAGER_OPEN : PAGER_READER); |
+ } |
if( USEFETCH(pPager) ) sqlite3OsUnfetch(pPager->fd, 0, 0); |
+ pPager->errCode = SQLITE_OK; |
+ setGetterMethod(pPager); |
} |
pPager->journalOff = 0; |
@@ -1859,6 +1905,7 @@ static int pager_error(Pager *pPager, int rc){ |
if( rc2==SQLITE_FULL || rc2==SQLITE_IOERR ){ |
pPager->errCode = rc; |
pPager->eState = PAGER_ERROR; |
+ setGetterMethod(pPager); |
} |
return rc; |
} |
@@ -1866,6 +1913,29 @@ static int pager_error(Pager *pPager, int rc){ |
static int pager_truncate(Pager *pPager, Pgno nPage); |
/* |
+** The write transaction open on pPager is being committed (bCommit==1) |
+** or rolled back (bCommit==0). |
+** |
+** Return TRUE if and only if all dirty pages should be flushed to disk. |
+** |
+** Rules: |
+** |
+** * For non-TEMP databases, always sync to disk. This is necessary |
+** for transactions to be durable. |
+** |
+** * Sync TEMP database only on a COMMIT (not a ROLLBACK) when the backing |
+** file has been created already (via a spill on pagerStress()) and |
+** when the number of dirty pages in memory exceeds 25% of the total |
+** cache size. |
+*/ |
+static int pagerFlushOnCommit(Pager *pPager, int bCommit){ |
+ if( pPager->tempFile==0 ) return 1; |
+ if( !bCommit ) return 0; |
+ if( !isOpen(pPager->fd) ) return 0; |
+ return (sqlite3PCachePercentDirty(pPager->pPCache)>=25); |
+} |
+ |
+/* |
** This routine ends a transaction. A transaction is usually ended by |
** either a COMMIT or a ROLLBACK operation. This routine may be called |
** after rollback of a hot-journal, or if an error occurs while opening |
@@ -1947,8 +2017,8 @@ static int pager_end_transaction(Pager *pPager, int hasMaster, int bCommit){ |
assert( !pagerUseWal(pPager) ); |
/* Finalize the journal file. */ |
- if( sqlite3IsMemJournal(pPager->jfd) ){ |
- assert( pPager->journalMode==PAGER_JOURNALMODE_MEMORY ); |
+ if( sqlite3JournalIsInMemory(pPager->jfd) ){ |
+ /* assert( pPager->journalMode==PAGER_JOURNALMODE_MEMORY ); */ |
sqlite3OsClose(pPager->jfd); |
}else if( pPager->journalMode==PAGER_JOURNALMODE_TRUNCATE ){ |
if( pPager->journalOff==0 ){ |
@@ -1968,22 +2038,23 @@ static int pager_end_transaction(Pager *pPager, int hasMaster, int bCommit){ |
}else if( pPager->journalMode==PAGER_JOURNALMODE_PERSIST |
|| (pPager->exclusiveMode && pPager->journalMode!=PAGER_JOURNALMODE_WAL) |
){ |
- rc = zeroJournalHdr(pPager, hasMaster); |
+ rc = zeroJournalHdr(pPager, hasMaster||pPager->tempFile); |
pPager->journalOff = 0; |
}else{ |
/* This branch may be executed with Pager.journalMode==MEMORY if |
** a hot-journal was just rolled back. In this case the journal |
** file should be closed and deleted. If this connection writes to |
- ** the database file, it will do so using an in-memory journal. |
+ ** the database file, it will do so using an in-memory journal. |
*/ |
- int bDelete = (!pPager->tempFile && sqlite3JournalExists(pPager->jfd)); |
+ int bDelete = !pPager->tempFile; |
+ assert( sqlite3JournalIsInMemory(pPager->jfd)==0 ); |
assert( pPager->journalMode==PAGER_JOURNALMODE_DELETE |
|| pPager->journalMode==PAGER_JOURNALMODE_MEMORY |
|| pPager->journalMode==PAGER_JOURNALMODE_WAL |
); |
sqlite3OsClose(pPager->jfd); |
if( bDelete ){ |
- rc = sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0); |
+ rc = sqlite3OsDelete(pPager->pVfs, pPager->zJournal, pPager->extraSync); |
} |
} |
} |
@@ -2002,8 +2073,14 @@ static int pager_end_transaction(Pager *pPager, int hasMaster, int bCommit){ |
sqlite3BitvecDestroy(pPager->pInJournal); |
pPager->pInJournal = 0; |
pPager->nRec = 0; |
- sqlite3PcacheCleanAll(pPager->pPCache); |
- sqlite3PcacheTruncate(pPager->pPCache, pPager->dbSize); |
+ if( rc==SQLITE_OK ){ |
+ if( MEMDB || pagerFlushOnCommit(pPager, bCommit) ){ |
+ sqlite3PcacheCleanAll(pPager->pPCache); |
+ }else{ |
+ sqlite3PcacheClearWritable(pPager->pPCache); |
+ } |
+ sqlite3PcacheTruncate(pPager->pPCache, pPager->dbSize); |
+ } |
if( pagerUseWal(pPager) ){ |
/* Drop the WAL write-lock, if any. Also, if the connection was in |
@@ -2287,7 +2364,7 @@ static int pager_playback_one_page( |
pPg = sqlite3PagerLookup(pPager, pgno); |
} |
assert( pPg || !MEMDB ); |
- assert( pPager->eState!=PAGER_OPEN || pPg==0 ); |
+ assert( pPager->eState!=PAGER_OPEN || pPg==0 || pPager->tempFile ); |
PAGERTRACE(("PLAYBACK %d page %d hash(%08x) %s\n", |
PAGERID(pPager), pgno, pager_datahash(pPager->pageSize, (u8*)aData), |
(isMainJrnl?"main-journal":"sub-journal") |
@@ -2309,9 +2386,9 @@ static int pager_playback_one_page( |
pPager->dbFileSize = pgno; |
} |
if( pPager->pBackup ){ |
- CODEC1(pPager, aData, pgno, 3, rc=SQLITE_NOMEM); |
+ CODEC1(pPager, aData, pgno, 3, rc=SQLITE_NOMEM_BKPT); |
sqlite3BackupUpdate(pPager->pBackup, pgno, (u8*)aData); |
- CODEC2(pPager, aData, pgno, 7, rc=SQLITE_NOMEM, aData); |
+ CODEC2(pPager, aData, pgno, 7, rc=SQLITE_NOMEM_BKPT, aData); |
} |
}else if( !isMainJrnl && pPg==0 ){ |
/* If this is a rollback of a savepoint and data was not written to |
@@ -2337,7 +2414,6 @@ static int pager_playback_one_page( |
assert( (pPager->doNotSpill & SPILLFLAG_ROLLBACK)!=0 ); |
pPager->doNotSpill &= ~SPILLFLAG_ROLLBACK; |
if( rc!=SQLITE_OK ) return rc; |
- pPg->flags &= ~PGHDR_NEED_READ; |
sqlite3PcacheMakeDirty(pPg); |
} |
if( pPg ){ |
@@ -2351,29 +2427,10 @@ static int pager_playback_one_page( |
pData = pPg->pData; |
memcpy(pData, (u8*)aData, pPager->pageSize); |
pPager->xReiniter(pPg); |
- if( isMainJrnl && (!isSavepnt || *pOffset<=pPager->journalHdr) ){ |
- /* If the contents of this page were just restored from the main |
- ** journal file, then its content must be as they were when the |
- ** transaction was first opened. In this case we can mark the page |
- ** as clean, since there will be no need to write it out to the |
- ** database. |
- ** |
- ** There is one exception to this rule. If the page is being rolled |
- ** back as part of a savepoint (or statement) rollback from an |
- ** unsynced portion of the main journal file, then it is not safe |
- ** to mark the page as clean. This is because marking the page as |
- ** clean will clear the PGHDR_NEED_SYNC flag. Since the page is |
- ** already in the journal file (recorded in Pager.pInJournal) and |
- ** the PGHDR_NEED_SYNC flag is cleared, if the page is written to |
- ** again within this transaction, it will be marked as dirty but |
- ** the PGHDR_NEED_SYNC flag will not be set. It could then potentially |
- ** be written out into the database file before its journal file |
- ** segment is synced. If a crash occurs during or following this, |
- ** database corruption may ensue. |
- */ |
- assert( !pagerUseWal(pPager) ); |
- sqlite3PcacheMakeClean(pPg); |
- } |
+ /* It used to be that sqlite3PcacheMakeClean(pPg) was called here. But |
+ ** that call was dangerous and had no detectable benefit since the cache |
+ ** is normally cleaned by sqlite3PcacheCleanAll() after rollback and so |
+ ** has been removed. */ |
pager_set_pagehash(pPg); |
/* If this was page 1, then restore the value of Pager.dbFileVers. |
@@ -2383,7 +2440,7 @@ static int pager_playback_one_page( |
} |
/* Decode the page just read from disk */ |
- CODEC1(pPager, pData, pPg->pgno, 3, rc=SQLITE_NOMEM); |
+ CODEC1(pPager, pData, pPg->pgno, 3, rc=SQLITE_NOMEM_BKPT); |
sqlite3PcacheRelease(pPg); |
} |
return rc; |
@@ -2449,7 +2506,7 @@ static int pager_delmaster(Pager *pPager, const char *zMaster){ |
pMaster = (sqlite3_file *)sqlite3MallocZero(pVfs->szOsFile * 2); |
pJournal = (sqlite3_file *)(((u8 *)pMaster) + pVfs->szOsFile); |
if( !pMaster ){ |
- rc = SQLITE_NOMEM; |
+ rc = SQLITE_NOMEM_BKPT; |
}else{ |
const int flags = (SQLITE_OPEN_READONLY|SQLITE_OPEN_MASTER_JOURNAL); |
rc = sqlite3OsOpen(pVfs, zMaster, pMaster, flags, 0); |
@@ -2466,7 +2523,7 @@ static int pager_delmaster(Pager *pPager, const char *zMaster){ |
nMasterPtr = pVfs->mxPathname+1; |
zMasterJournal = sqlite3Malloc(nMasterJournal + nMasterPtr + 1); |
if( !zMasterJournal ){ |
- rc = SQLITE_NOMEM; |
+ rc = SQLITE_NOMEM_BKPT; |
goto delmaster_out; |
} |
zMasterPtr = &zMasterJournal[nMasterJournal+1]; |
@@ -2714,7 +2771,7 @@ static int pager_playback(Pager *pPager, int isHot){ |
** TODO: Technically the following is an error because it assumes that |
** buffer Pager.pTmpSpace is (mxPathname+1) bytes or larger. i.e. that |
** (pPager->pageSize >= pPager->pVfs->mxPathname+1). Using os_unix.c, |
- ** mxPathname is 512, which is the same as the minimum allowable value |
+ ** mxPathname is 512, which is the same as the minimum allowable value |
** for pageSize. |
*/ |
zMaster = pPager->pTmpSpace; |
@@ -2936,7 +2993,7 @@ static int readDbPage(PgHdr *pPg, u32 iFrame){ |
memcpy(&pPager->dbFileVers, dbFileVers, sizeof(pPager->dbFileVers)); |
} |
} |
- CODEC1(pPager, pPg->pData, pgno, 3, rc = SQLITE_NOMEM); |
+ CODEC1(pPager, pPg->pData, pgno, 3, rc = SQLITE_NOMEM_BKPT); |
PAGER_INCR(sqlite3_pager_readdb_count); |
PAGER_INCR(pPager->nRead); |
@@ -3164,6 +3221,8 @@ static int pagerPagecount(Pager *pPager, Pgno *pnPage){ |
*/ |
assert( pPager->eState==PAGER_OPEN ); |
assert( pPager->eLock>=SHARED_LOCK ); |
+ assert( isOpen(pPager->fd) ); |
+ assert( pPager->tempFile==0 ); |
nPage = sqlite3WalDbsize(pPager->pWal); |
/* If the number of pages in the database is not available from the |
@@ -3171,14 +3230,11 @@ static int pagerPagecount(Pager *pPager, Pgno *pnPage){ |
** 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 ){ |
+ if( nPage==0 && ALWAYS(isOpen(pPager->fd)) ){ |
i64 n = 0; /* Size of db file in bytes */ |
- assert( isOpen(pPager->fd) || pPager->tempFile ); |
- if( isOpen(pPager->fd) ){ |
- int rc = sqlite3OsFileSize(pPager->fd, &n); |
- if( rc!=SQLITE_OK ){ |
- return rc; |
- } |
+ int rc = sqlite3OsFileSize(pPager->fd, &n); |
+ if( rc!=SQLITE_OK ){ |
+ return rc; |
} |
nPage = (Pgno)((n+pPager->pageSize-1) / pPager->pageSize); |
} |
@@ -3296,7 +3352,7 @@ static int pagerPlaybackSavepoint(Pager *pPager, PagerSavepoint *pSavepoint){ |
if( pSavepoint ){ |
pDone = sqlite3BitvecCreate(pSavepoint->nOrig); |
if( !pDone ){ |
- return SQLITE_NOMEM; |
+ return SQLITE_NOMEM_BKPT; |
} |
} |
@@ -3417,6 +3473,7 @@ static void pagerFixMaplimit(Pager *pPager){ |
sqlite3_int64 sz; |
sz = pPager->szMmap; |
pPager->bUseFetch = (sz>0); |
+ setGetterMethod(pPager); |
sqlite3OsFileControlHint(pPager->fd, SQLITE_FCNTL_MMAP_SIZE, &sz); |
} |
#endif |
@@ -3443,7 +3500,7 @@ void sqlite3PagerShrink(Pager *pPager){ |
** The "level" in pgFlags & PAGER_SYNCHRONOUS_MASK sets the robustness |
** of the database to damage due to OS crashes or power failures by |
** changing the number of syncs()s when writing the journals. |
-** There are three levels: |
+** There are four levels: |
** |
** OFF sqlite3OsSync() is never called. This is the default |
** for temporary and transient files. |
@@ -3463,6 +3520,10 @@ void sqlite3PagerShrink(Pager *pPager){ |
** assurance that the journal will not be corrupted to the |
** point of causing damage to the database during rollback. |
** |
+** EXTRA This is like FULL except that is also syncs the directory |
+** that contains the rollback journal after the rollback |
+** journal is unlinked. |
+** |
** The above is for a rollback-journal mode. For WAL mode, OFF continues |
** to mean that no syncs ever occur. NORMAL means that the WAL is synced |
** prior to the start of checkpoint and that the database file is synced |
@@ -3470,7 +3531,8 @@ void sqlite3PagerShrink(Pager *pPager){ |
** was written back into the database. But no sync operations occur for |
** an ordinary commit in NORMAL mode with WAL. FULL means that the WAL |
** file is synced following each commit operation, in addition to the |
-** syncs associated with NORMAL. |
+** syncs associated with NORMAL. There is no difference between FULL |
+** and EXTRA for WAL mode. |
** |
** Do not confuse synchronous=FULL with SQLITE_SYNC_FULL. The |
** SQLITE_SYNC_FULL macro means to use the MacOSX-style full-fsync |
@@ -3489,9 +3551,15 @@ void sqlite3PagerSetFlags( |
unsigned pgFlags /* Various flags */ |
){ |
unsigned level = pgFlags & PAGER_SYNCHRONOUS_MASK; |
- assert( level>=1 && level<=3 ); |
- pPager->noSync = (level==1 || pPager->tempFile) ?1:0; |
- pPager->fullSync = (level==3 && !pPager->tempFile) ?1:0; |
+ if( pPager->tempFile ){ |
+ pPager->noSync = 1; |
+ pPager->fullSync = 0; |
+ pPager->extraSync = 0; |
+ }else{ |
+ pPager->noSync = level==PAGER_SYNCHRONOUS_OFF ?1:0; |
+ pPager->fullSync = level>=PAGER_SYNCHRONOUS_FULL ?1:0; |
+ pPager->extraSync = level==PAGER_SYNCHRONOUS_EXTRA ?1:0; |
+ } |
if( pPager->noSync ){ |
pPager->syncFlags = 0; |
pPager->ckptSyncFlags = 0; |
@@ -3653,7 +3721,7 @@ int sqlite3PagerSetPagesize(Pager *pPager, u32 *pPageSize, int nReserve){ |
} |
if( rc==SQLITE_OK ){ |
pNew = (char *)sqlite3PageMalloc(pageSize); |
- if( !pNew ) rc = SQLITE_NOMEM; |
+ if( !pNew ) rc = SQLITE_NOMEM_BKPT; |
} |
if( rc==SQLITE_OK ){ |
@@ -3902,6 +3970,7 @@ static int pagerSyncHotJournal(Pager *pPager){ |
return rc; |
} |
+#if SQLITE_MAX_MMAP_SIZE>0 |
/* |
** Obtain a reference to a memory mapped page object for page number pgno. |
** The new object will use the pointer pData, obtained from xFetch(). |
@@ -3924,12 +3993,13 @@ static int pagerAcquireMapPage( |
*ppPage = p = pPager->pMmapFreelist; |
pPager->pMmapFreelist = p->pDirty; |
p->pDirty = 0; |
- memset(p->pExtra, 0, pPager->nExtra); |
+ assert( pPager->nExtra>=8 ); |
+ memset(p->pExtra, 0, 8); |
}else{ |
*ppPage = p = (PgHdr *)sqlite3MallocZero(sizeof(PgHdr) + pPager->nExtra); |
if( p==0 ){ |
sqlite3OsUnfetch(pPager->fd, (i64)(pgno-1) * pPager->pageSize, pData); |
- return SQLITE_NOMEM; |
+ return SQLITE_NOMEM_BKPT; |
} |
p->pExtra = (void *)&p[1]; |
p->flags = PGHDR_MMAP; |
@@ -3949,6 +4019,7 @@ static int pagerAcquireMapPage( |
return SQLITE_OK; |
} |
+#endif |
/* |
** Release a reference to page pPg. pPg must have been returned by an |
@@ -3991,9 +4062,10 @@ static void pagerFreeMapHdrs(Pager *pPager){ |
** a hot journal may be left in the filesystem but no error is returned |
** to the caller. |
*/ |
-int sqlite3PagerClose(Pager *pPager){ |
+int sqlite3PagerClose(Pager *pPager, sqlite3 *db){ |
u8 *pTmp = (u8 *)pPager->pTmpSpace; |
+ assert( db || pagerUseWal(pPager)==0 ); |
assert( assert_pager_state(pPager) ); |
disable_simulated_io_errors(); |
sqlite3BeginBenignMalloc(); |
@@ -4001,7 +4073,10 @@ int sqlite3PagerClose(Pager *pPager){ |
/* pPager->errCode = 0; */ |
pPager->exclusiveMode = 0; |
#ifndef SQLITE_OMIT_WAL |
- sqlite3WalClose(pPager->pWal, pPager->ckptSyncFlags, pPager->pageSize, pTmp); |
+ assert( db || pPager->pWal==0 ); |
+ sqlite3WalClose(pPager->pWal, db, pPager->ckptSyncFlags, pPager->pageSize, |
+ (db && (db->flags & SQLITE_NoCkptOnClose) ? 0 : pTmp) |
+ ); |
pPager->pWal = 0; |
#endif |
pager_reset(pPager); |
@@ -4243,8 +4318,9 @@ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){ |
/* This function is only called for rollback pagers in WRITER_DBMOD state. */ |
assert( !pagerUseWal(pPager) ); |
- assert( pPager->eState==PAGER_WRITER_DBMOD ); |
+ assert( pPager->tempFile || pPager->eState==PAGER_WRITER_DBMOD ); |
assert( pPager->eLock==EXCLUSIVE_LOCK ); |
+ assert( isOpen(pPager->fd) || pList->pDirty==0 ); |
/* If the file is a temp-file has not yet been opened, open it now. It |
** is not possible for rc to be other than SQLITE_OK if this branch |
@@ -4287,7 +4363,7 @@ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){ |
if( pList->pgno==1 ) pager_write_changecounter(pList); |
/* Encode the database */ |
- CODEC2(pPager, pList->pData, pgno, 6, return SQLITE_NOMEM, pData); |
+ CODEC2(pPager, pList->pData, pgno, 6, return SQLITE_NOMEM_BKPT, pData); |
/* Write out the page data. */ |
rc = sqlite3OsWrite(pPager->fd, pData, pPager->pageSize, offset); |
@@ -4332,11 +4408,14 @@ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){ |
static int openSubJournal(Pager *pPager){ |
int rc = SQLITE_OK; |
if( !isOpen(pPager->sjfd) ){ |
+ const int flags = SQLITE_OPEN_SUBJOURNAL | SQLITE_OPEN_READWRITE |
+ | SQLITE_OPEN_CREATE | SQLITE_OPEN_EXCLUSIVE |
+ | SQLITE_OPEN_DELETEONCLOSE; |
+ int nStmtSpill = sqlite3Config.nStmtSpill; |
if( pPager->journalMode==PAGER_JOURNALMODE_MEMORY || pPager->subjInMemory ){ |
- sqlite3MemJournalOpen(pPager->sjfd); |
- }else{ |
- rc = pagerOpentemp(pPager, pPager->sjfd, SQLITE_OPEN_SUBJOURNAL); |
+ nStmtSpill = -1; |
} |
+ rc = sqlite3JournalOpen(pPager->pVfs, 0, pPager->sjfd, flags, nStmtSpill); |
} |
return rc; |
} |
@@ -4374,7 +4453,7 @@ static int subjournalPage(PgHdr *pPg){ |
i64 offset = (i64)pPager->nSubRec*(4+pPager->pageSize); |
char *pData2; |
- CODEC2(pPager, pData, pPg->pgno, 7, return SQLITE_NOMEM, pData2); |
+ CODEC2(pPager, pData, pPg->pgno, 7, return SQLITE_NOMEM_BKPT, pData2); |
PAGERTRACE(("STMT-JOURNAL %d page %d\n", PAGERID(pPager), pPg->pgno)); |
rc = write32bits(pPager->sjfd, offset, pPg->pgno); |
if( rc==SQLITE_OK ){ |
@@ -4516,7 +4595,9 @@ int sqlite3PagerFlush(Pager *pPager){ |
** |
** The nExtra parameter specifies the number of bytes of space allocated |
** along with each page reference. This space is available to the user |
-** via the sqlite3PagerGetExtra() API. |
+** via the sqlite3PagerGetExtra() API. When a new page is allocated, the |
+** first 8 bytes of this space are zeroed but the remainder is uninitialized. |
+** (The extra space is used by btree as the MemPage object.) |
** |
** The flags argument is used to specify properties that affect the |
** operation of the pager. It should be passed some bitwise combination |
@@ -4557,18 +4638,8 @@ int sqlite3PagerOpen( |
int nUri = 0; /* Number of bytes of URI args at *zUri */ |
/* Figure out how much space is required for each journal file-handle |
- ** (there are two of them, the main journal and the sub-journal). This |
- ** is the maximum space required for an in-memory journal file handle |
- ** and a regular journal file-handle. Note that a "regular journal-handle" |
- ** may be a wrapper capable of caching the first portion of the journal |
- ** file in memory to implement the atomic-write optimization (see |
- ** source file journal.c). |
- */ |
- if( sqlite3JournalSize(pVfs)>sqlite3MemJournalSize() ){ |
- journalFileSize = ROUND8(sqlite3JournalSize(pVfs)); |
- }else{ |
- journalFileSize = ROUND8(sqlite3MemJournalSize()); |
- } |
+ ** (there are two of them, the main journal and the sub-journal). */ |
+ journalFileSize = ROUND8(sqlite3JournalSize(pVfs)); |
/* Set the output variable to NULL in case an error occurs. */ |
*ppPager = 0; |
@@ -4578,7 +4649,7 @@ int sqlite3PagerOpen( |
memDb = 1; |
if( zFilename && zFilename[0] ){ |
zPathname = sqlite3DbStrDup(0, zFilename); |
- if( zPathname==0 ) return SQLITE_NOMEM; |
+ if( zPathname==0 ) return SQLITE_NOMEM_BKPT; |
nPathname = sqlite3Strlen30(zPathname); |
zFilename = 0; |
} |
@@ -4594,7 +4665,7 @@ int sqlite3PagerOpen( |
nPathname = pVfs->mxPathname+1; |
zPathname = sqlite3DbMallocRaw(0, nPathname*2); |
if( zPathname==0 ){ |
- return SQLITE_NOMEM; |
+ return SQLITE_NOMEM_BKPT; |
} |
zPathname[0] = 0; /* Make sure initialized even if FullPathname() fails */ |
rc = sqlite3OsFullPathname(pVfs, zFilename, nPathname, zPathname); |
@@ -4647,7 +4718,7 @@ int sqlite3PagerOpen( |
assert( EIGHT_BYTE_ALIGNMENT(SQLITE_INT_TO_PTR(journalFileSize)) ); |
if( !pPtr ){ |
sqlite3DbFree(0, zPathname); |
- return SQLITE_NOMEM; |
+ return SQLITE_NOMEM_BKPT; |
} |
pPager = (Pager*)(pPtr); |
pPager->pPCache = (PCache*)(pPtr += ROUND8(sizeof(*pPager))); |
@@ -4756,8 +4827,8 @@ act_like_temp_file: |
/* Initialize the PCache object. */ |
if( rc==SQLITE_OK ){ |
- assert( nExtra<1000 ); |
nExtra = ROUND8(nExtra); |
+ assert( nExtra>=8 && nExtra<1000 ); |
rc = sqlite3PcacheOpen(szPageDflt, nExtra, !memDb, |
!memDb?pagerStress:0, (void *)pPager, pPager->pPCache); |
} |
@@ -4796,11 +4867,13 @@ act_like_temp_file: |
pPager->noSync = pPager->tempFile; |
if( pPager->noSync ){ |
assert( pPager->fullSync==0 ); |
+ assert( pPager->extraSync==0 ); |
assert( pPager->syncFlags==0 ); |
assert( pPager->walSyncFlags==0 ); |
assert( pPager->ckptSyncFlags==0 ); |
}else{ |
pPager->fullSync = 1; |
+ pPager->extraSync = 0; |
pPager->syncFlags = SQLITE_SYNC_NORMAL; |
pPager->walSyncFlags = SQLITE_SYNC_NORMAL | WAL_SYNC_TRANSACTIONS; |
pPager->ckptSyncFlags = SQLITE_SYNC_NORMAL; |
@@ -4820,6 +4893,7 @@ act_like_temp_file: |
/* pPager->xBusyHandler = 0; */ |
/* pPager->pBusyHandlerArg = 0; */ |
pPager->xReiniter = xReinit; |
+ setGetterMethod(pPager); |
/* memset(pPager->aHash, 0, sizeof(pPager->aHash)); */ |
/* pPager->szMmap = SQLITE_DEFAULT_MMAP_SIZE // will be set by btree.c */ |
@@ -4917,6 +4991,7 @@ static int hasHotJournal(Pager *pPager, int *pExists){ |
if( rc==SQLITE_OK && !locked ){ |
Pgno nPage; /* Number of pages in database file */ |
+ assert( pPager->tempFile==0 ); |
rc = pagerPagecount(pPager, &nPage); |
if( rc==SQLITE_OK ){ |
/* If the database is zero pages in size, that means that either (1) the |
@@ -5009,17 +5084,17 @@ int sqlite3PagerSharedLock(Pager *pPager){ |
/* This routine is only called from b-tree and only when there are no |
** outstanding pages. This implies that the pager state should either |
** be OPEN or READER. READER is only possible if the pager is or was in |
- ** exclusive access mode. |
- */ |
+ ** exclusive access mode. */ |
assert( sqlite3PcacheRefCount(pPager->pPCache)==0 ); |
assert( assert_pager_state(pPager) ); |
assert( pPager->eState==PAGER_OPEN || pPager->eState==PAGER_READER ); |
- if( NEVER(MEMDB && pPager->errCode) ){ return pPager->errCode; } |
+ assert( pPager->errCode==SQLITE_OK ); |
if( !pagerUseWal(pPager) && pPager->eState==PAGER_OPEN ){ |
int bHotJournal = 1; /* True if there exists a hot journal-file */ |
assert( !MEMDB ); |
+ assert( pPager->tempFile==0 || pPager->eLock==EXCLUSIVE_LOCK ); |
rc = pager_wait_on_lock(pPager, SHARED_LOCK); |
if( rc!=SQLITE_OK ){ |
@@ -5105,7 +5180,7 @@ int sqlite3PagerSharedLock(Pager *pPager){ |
assert( rc==SQLITE_OK ); |
rc = pagerSyncHotJournal(pPager); |
if( rc==SQLITE_OK ){ |
- rc = pager_playback(pPager, 1); |
+ rc = pager_playback(pPager, !pPager->tempFile); |
pPager->eState = PAGER_OPEN; |
} |
}else if( !pPager->exclusiveMode ){ |
@@ -5201,7 +5276,7 @@ int sqlite3PagerSharedLock(Pager *pPager){ |
rc = pagerBeginReadTransaction(pPager); |
} |
- if( pPager->eState==PAGER_OPEN && rc==SQLITE_OK ){ |
+ if( pPager->tempFile==0 && pPager->eState==PAGER_OPEN && rc==SQLITE_OK ){ |
rc = pagerPagecount(pPager, &pPager->dbSize); |
} |
@@ -5232,10 +5307,17 @@ static void pagerUnlockIfUnused(Pager *pPager){ |
} |
/* |
-** Acquire a reference to page number pgno in pager pPager (a page |
-** reference has type DbPage*). If the requested reference is |
+** The page getter methods each try to acquire a reference to a |
+** page with page number pgno. If the requested reference is |
** successfully obtained, it is copied to *ppPage and SQLITE_OK returned. |
** |
+** There are different implementations of the getter method depending |
+** on the current state of the pager. |
+** |
+** getPageNormal() -- The normal getter |
+** getPageError() -- Used if the pager is in an error state |
+** getPageMmap() -- Used if memory-mapped I/O is enabled |
+** |
** If the requested page is already in the cache, it is returned. |
** Otherwise, a new page object is allocated and populated with data |
** read from the database file. In some cases, the pcache module may |
@@ -5247,14 +5329,14 @@ static void pagerUnlockIfUnused(Pager *pPager){ |
** already in the cache when this function is called, then the extra |
** data is left as it was when the page object was last used. |
** |
-** If the database image is smaller than the requested page or if a |
-** non-zero value is passed as the noContent parameter and the |
+** If the database image is smaller than the requested page or if |
+** the flags parameter contains the PAGER_GET_NOCONTENT bit and the |
** requested page is not already stored in the cache, then no |
** actual disk read occurs. In this case the memory image of the |
** page is initialized to all zeros. |
** |
-** If noContent is true, it means that we do not care about the contents |
-** of the page. This occurs in two scenarios: |
+** If PAGER_GET_NOCONTENT is true, it means that we do not care about |
+** the contents of the page. This occurs in two scenarios: |
** |
** a) When reading a free-list leaf page from the database, and |
** |
@@ -5262,8 +5344,8 @@ static void pagerUnlockIfUnused(Pager *pPager){ |
** a new page into the cache to be filled with the data read |
** from the savepoint journal. |
** |
-** If noContent is true, then the data returned is zeroed instead of |
-** being read from the database. Additionally, the bits corresponding |
+** If PAGER_GET_NOCONTENT is true, then the data returned is zeroed instead |
+** of being read from the database. Additionally, the bits corresponding |
** to pgno in Pager.pInJournal (bitvec of pages already written to the |
** journal file) and the PagerSavepoint.pInSavepoint bitvecs of any open |
** savepoints are set. This means if the page is made writable at any |
@@ -5281,106 +5363,39 @@ static void pagerUnlockIfUnused(Pager *pPager){ |
** Since Lookup() never goes to disk, it never has to deal with locks |
** or journal files. |
*/ |
-int sqlite3PagerGet( |
+static int getPageNormal( |
Pager *pPager, /* The pager open on the database file */ |
Pgno pgno, /* Page number to fetch */ |
DbPage **ppPage, /* Write a pointer to the page here */ |
int flags /* PAGER_GET_XXX flags */ |
){ |
int rc = SQLITE_OK; |
- PgHdr *pPg = 0; |
- u32 iFrame = 0; /* Frame to read from WAL file */ |
- const int noContent = (flags & PAGER_GET_NOCONTENT); |
- |
- /* It is acceptable to use a read-only (mmap) page for any page except |
- ** 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) |
- && (pPager->eState==PAGER_READER || (flags & PAGER_GET_READONLY)) |
-#ifdef SQLITE_HAS_CODEC |
- && pPager->xCodec==0 |
-#endif |
- ); |
+ PgHdr *pPg; |
+ u8 noContent; /* True if PAGER_GET_NOCONTENT is set */ |
+ sqlite3_pcache_page *pBase; |
- /* 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->errCode==SQLITE_OK ); |
assert( pPager->eState>=PAGER_READER ); |
assert( assert_pager_state(pPager) ); |
- assert( noContent==0 || bMmapOk==0 ); |
- |
assert( pPager->hasHeldSharedLock==1 ); |
- /* If the pager is in the error state, return an error immediately. |
- ** Otherwise, request the page from the PCache layer. */ |
- if( pPager->errCode!=SQLITE_OK ){ |
- rc = pPager->errCode; |
- }else{ |
- if( bMmapOk && pagerUseWal(pPager) ){ |
- rc = sqlite3WalFindFrame(pPager->pWal, pgno, &iFrame); |
- if( rc!=SQLITE_OK ) goto pager_acquire_err; |
- } |
- |
- if( bMmapOk && iFrame==0 ){ |
- void *pData = 0; |
- |
- rc = sqlite3OsFetch(pPager->fd, |
- (i64)(pgno-1) * pPager->pageSize, pPager->pageSize, &pData |
- ); |
- |
- if( rc==SQLITE_OK && pData ){ |
- if( pPager->eState>PAGER_READER ){ |
- pPg = sqlite3PagerLookup(pPager, pgno); |
- } |
- if( pPg==0 ){ |
- rc = pagerAcquireMapPage(pPager, pgno, pData, &pPg); |
- }else{ |
- sqlite3OsUnfetch(pPager->fd, (i64)(pgno-1)*pPager->pageSize, pData); |
- } |
- if( pPg ){ |
- assert( rc==SQLITE_OK ); |
- *ppPage = pPg; |
- return SQLITE_OK; |
- } |
- } |
- if( rc!=SQLITE_OK ){ |
- goto pager_acquire_err; |
- } |
- } |
- |
- { |
- sqlite3_pcache_page *pBase; |
- pBase = sqlite3PcacheFetch(pPager->pPCache, pgno, 3); |
- 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); |
- assert( pPg!=0 ); |
- } |
- } |
- |
- if( rc!=SQLITE_OK ){ |
- /* Either the call to sqlite3PcacheFetch() returned an error or the |
- ** pager was already in the error-state when this function was called. |
- ** Set pPg to 0 and jump to the exception handler. */ |
+ if( pgno==0 ) return SQLITE_CORRUPT_BKPT; |
+ pBase = sqlite3PcacheFetch(pPager->pPCache, pgno, 3); |
+ if( pBase==0 ){ |
pPg = 0; |
- goto pager_acquire_err; |
+ rc = sqlite3PcacheFetchStress(pPager->pPCache, pgno, &pBase); |
+ if( rc!=SQLITE_OK ) goto pager_acquire_err; |
+ if( pBase==0 ){ |
+ rc = SQLITE_NOMEM_BKPT; |
+ goto pager_acquire_err; |
+ } |
} |
+ pPg = *ppPage = sqlite3PcacheFetchFinish(pPager->pPCache, pgno, pBase); |
assert( pPg==(*ppPage) ); |
assert( pPg->pgno==pgno ); |
assert( pPg->pPager==pPager || pPg->pPager==0 ); |
+ noContent = (flags & PAGER_GET_NOCONTENT)!=0; |
if( pPg->pPager && !noContent ){ |
/* In this case the pcache already contains an initialized copy of |
** the page. Return without further ado. */ |
@@ -5390,18 +5405,20 @@ int sqlite3PagerGet( |
}else{ |
/* The pager cache has created a new page. Its content needs to |
- ** be initialized. */ |
- |
- pPg->pPager = pPager; |
- |
- /* The maximum page number is 2^31. Return SQLITE_CORRUPT if a page |
- ** number greater than this, or the unused locking-page, is requested. */ |
+ ** be initialized. But first some error checks: |
+ ** |
+ ** (1) The maximum page number is 2^31 |
+ ** (2) Never try to fetch the locking page |
+ */ |
if( pgno>PAGER_MAX_PGNO || pgno==PAGER_MJ_PGNO(pPager) ){ |
rc = SQLITE_CORRUPT_BKPT; |
goto pager_acquire_err; |
} |
- if( MEMDB || pPager->dbSize<pgno || noContent || !isOpen(pPager->fd) ){ |
+ pPg->pPager = pPager; |
+ |
+ assert( !isOpen(pPager->fd) || !MEMDB ); |
+ if( !isOpen(pPager->fd) || pPager->dbSize<pgno || noContent ){ |
if( pgno>pPager->mxPgno ){ |
rc = SQLITE_FULL; |
goto pager_acquire_err; |
@@ -5425,7 +5442,8 @@ int sqlite3PagerGet( |
memset(pPg->pData, 0, pPager->pageSize); |
IOTRACE(("ZERO %p %d\n", pPager, pgno)); |
}else{ |
- if( pagerUseWal(pPager) && bMmapOk==0 ){ |
+ u32 iFrame = 0; /* Frame to read from WAL file */ |
+ if( pagerUseWal(pPager) ){ |
rc = sqlite3WalFindFrame(pPager->pWal, pgno, &iFrame); |
if( rc!=SQLITE_OK ) goto pager_acquire_err; |
} |
@@ -5438,7 +5456,6 @@ int sqlite3PagerGet( |
} |
pager_set_pagehash(pPg); |
} |
- |
return SQLITE_OK; |
pager_acquire_err: |
@@ -5447,11 +5464,109 @@ pager_acquire_err: |
sqlite3PcacheDrop(pPg); |
} |
pagerUnlockIfUnused(pPager); |
- |
*ppPage = 0; |
return rc; |
} |
+#if SQLITE_MAX_MMAP_SIZE>0 |
+/* The page getter for when memory-mapped I/O is enabled */ |
+static int getPageMMap( |
+ Pager *pPager, /* The pager open on the database file */ |
+ Pgno pgno, /* Page number to fetch */ |
+ DbPage **ppPage, /* Write a pointer to the page here */ |
+ int flags /* PAGER_GET_XXX flags */ |
+){ |
+ int rc = SQLITE_OK; |
+ PgHdr *pPg = 0; |
+ u32 iFrame = 0; /* Frame to read from WAL file */ |
+ |
+ /* It is acceptable to use a read-only (mmap) page for any page except |
+ ** 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 |
+ && (pPager->eState==PAGER_READER || (flags & PAGER_GET_READONLY)) |
+ ); |
+ |
+ assert( USEFETCH(pPager) ); |
+#ifdef SQLITE_HAS_CODEC |
+ assert( 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( pPager->hasHeldSharedLock==1 ); |
+ assert( pPager->errCode==SQLITE_OK ); |
+ |
+ if( bMmapOk && pagerUseWal(pPager) ){ |
+ rc = sqlite3WalFindFrame(pPager->pWal, pgno, &iFrame); |
+ if( rc!=SQLITE_OK ){ |
+ *ppPage = 0; |
+ return rc; |
+ } |
+ } |
+ if( bMmapOk && iFrame==0 ){ |
+ void *pData = 0; |
+ rc = sqlite3OsFetch(pPager->fd, |
+ (i64)(pgno-1) * pPager->pageSize, pPager->pageSize, &pData |
+ ); |
+ if( rc==SQLITE_OK && pData ){ |
+ if( pPager->eState>PAGER_READER || pPager->tempFile ){ |
+ pPg = sqlite3PagerLookup(pPager, pgno); |
+ } |
+ if( pPg==0 ){ |
+ rc = pagerAcquireMapPage(pPager, pgno, pData, &pPg); |
+ }else{ |
+ sqlite3OsUnfetch(pPager->fd, (i64)(pgno-1)*pPager->pageSize, pData); |
+ } |
+ if( pPg ){ |
+ assert( rc==SQLITE_OK ); |
+ *ppPage = pPg; |
+ return SQLITE_OK; |
+ } |
+ } |
+ if( rc!=SQLITE_OK ){ |
+ *ppPage = 0; |
+ return rc; |
+ } |
+ } |
+ return getPageNormal(pPager, pgno, ppPage, flags); |
+} |
+#endif /* SQLITE_MAX_MMAP_SIZE>0 */ |
+ |
+/* The page getter method for when the pager is an error state */ |
+static int getPageError( |
+ Pager *pPager, /* The pager open on the database file */ |
+ Pgno pgno, /* Page number to fetch */ |
+ DbPage **ppPage, /* Write a pointer to the page here */ |
+ int flags /* PAGER_GET_XXX flags */ |
+){ |
+ UNUSED_PARAMETER(pgno); |
+ UNUSED_PARAMETER(flags); |
+ assert( pPager->errCode!=SQLITE_OK ); |
+ *ppPage = 0; |
+ return pPager->errCode; |
+} |
+ |
+ |
+/* Dispatch all page fetch requests to the appropriate getter method. |
+*/ |
+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 */ |
+ int flags /* PAGER_GET_XXX flags */ |
+){ |
+ return pPager->xGet(pPager, pgno, ppPage, flags); |
+} |
+ |
/* |
** Acquire a page if it is already in the in-memory cache. Do |
** not read the page from disk. Return a pointer to the page, |
@@ -5535,7 +5650,7 @@ static int pager_open_journal(Pager *pPager){ |
if( !pagerUseWal(pPager) && pPager->journalMode!=PAGER_JOURNALMODE_OFF ){ |
pPager->pInJournal = sqlite3BitvecCreate(pPager->dbSize); |
if( pPager->pInJournal==0 ){ |
- return SQLITE_NOMEM; |
+ return SQLITE_NOMEM_BKPT; |
} |
/* Open the journal file if it is not already open. */ |
@@ -5543,24 +5658,24 @@ static int pager_open_journal(Pager *pPager){ |
if( pPager->journalMode==PAGER_JOURNALMODE_MEMORY ){ |
sqlite3MemJournalOpen(pPager->jfd); |
}else{ |
- const int flags = /* VFS flags to open journal file */ |
- SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE| |
- (pPager->tempFile ? |
- (SQLITE_OPEN_DELETEONCLOSE|SQLITE_OPEN_TEMP_JOURNAL): |
- (SQLITE_OPEN_MAIN_JOURNAL) |
- ); |
+ int flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE; |
+ int nSpill; |
+ if( pPager->tempFile ){ |
+ flags |= (SQLITE_OPEN_DELETEONCLOSE|SQLITE_OPEN_TEMP_JOURNAL); |
+ nSpill = sqlite3Config.nStmtSpill; |
+ }else{ |
+ flags |= SQLITE_OPEN_MAIN_JOURNAL; |
+ nSpill = jrnlBufferSize(pPager); |
+ } |
+ |
/* Verify that the database still has the same name as it did when |
** it was originally opened. */ |
rc = databaseIsUnmoved(pPager); |
if( rc==SQLITE_OK ){ |
-#ifdef SQLITE_ENABLE_ATOMIC_WRITE |
- rc = sqlite3JournalOpen( |
- pVfs, pPager->zJournal, pPager->jfd, flags, jrnlBufferSize(pPager) |
+ rc = sqlite3JournalOpen ( |
+ pVfs, pPager->zJournal, pPager->jfd, flags, nSpill |
); |
-#else |
- rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, flags, 0); |
-#endif |
} |
} |
assert( rc!=SQLITE_OK || isOpen(pPager->jfd) ); |
@@ -5690,7 +5805,7 @@ static SQLITE_NOINLINE int pagerAddPageToRollbackJournal(PgHdr *pPg){ |
assert( pPg->pgno!=PAGER_MJ_PGNO(pPager) ); |
assert( pPager->journalHdr<=pPager->journalOff ); |
- CODEC2(pPager, pPg->pData, pPg->pgno, 7, return SQLITE_NOMEM, pData2); |
+ CODEC2(pPager, pPg->pData, pPg->pgno, 7, return SQLITE_NOMEM_BKPT, pData2); |
cksum = pager_cksum(pPager, (u8*)pData2); |
/* Even if an IO or diskfull error occurs while journalling the |
@@ -5925,12 +6040,13 @@ int sqlite3PagerWrite(PgHdr *pPg){ |
assert( (pPg->flags & PGHDR_MMAP)==0 ); |
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( (pPg->flags & PGHDR_WRITEABLE)!=0 && pPager->dbSize>=pPg->pgno ){ |
if( pPager->nSavepoint ) return subjournalPageIfRequired(pPg); |
return SQLITE_OK; |
+ }else if( pPager->errCode ){ |
+ return pPager->errCode; |
}else if( pPager->sectorSize > (u32)pPager->pageSize ){ |
+ assert( pPager->tempFile==0 ); |
return pagerWriteLargeSector(pPg); |
}else{ |
return pager_write(pPg); |
@@ -5961,14 +6077,21 @@ int sqlite3PagerIswriteable(DbPage *pPg){ |
** |
** Tests show that this optimization can quadruple the speed of large |
** DELETE operations. |
+** |
+** This optimization cannot be used with a temp-file, as the page may |
+** have been dirty at the start of the transaction. In that case, if |
+** memory pressure forces page pPg out of the cache, the data does need |
+** to be written out to disk so that it may be read back in if the |
+** current transaction is rolled back. |
*/ |
void sqlite3PagerDontWrite(PgHdr *pPg){ |
Pager *pPager = pPg->pPager; |
- if( (pPg->flags&PGHDR_DIRTY) && pPager->nSavepoint==0 ){ |
+ if( !pPager->tempFile && (pPg->flags&PGHDR_DIRTY) && pPager->nSavepoint==0 ){ |
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; |
+ testcase( pPg->flags & PGHDR_NEED_SYNC ); |
pager_set_pagehash(pPg); |
} |
} |
@@ -6047,7 +6170,7 @@ static int pager_incr_changecounter(Pager *pPager, int isDirectMode){ |
if( DIRECT_MODE ){ |
const void *zBuf; |
assert( pPager->dbFileSize>0 ); |
- CODEC2(pPager, pPgHdr->pData, 1, 6, rc=SQLITE_NOMEM, zBuf); |
+ CODEC2(pPager, pPgHdr->pData, 1, 6, rc=SQLITE_NOMEM_BKPT, zBuf); |
if( rc==SQLITE_OK ){ |
rc = sqlite3OsWrite(pPager->fd, zBuf, pPager->pageSize, 0); |
pPager->aStat[PAGER_STAT_WRITE]++; |
@@ -6163,17 +6286,21 @@ int sqlite3PagerCommitPhaseOne( |
/* If a prior error occurred, report that error again. */ |
if( NEVER(pPager->errCode) ) return pPager->errCode; |
+ /* Provide the ability to easily simulate an I/O error during testing */ |
+ if( sqlite3FaultSim(400) ) return SQLITE_IOERR; |
+ |
PAGERTRACE(("DATABASE SYNC: File=%s zMaster=%s nSize=%d\n", |
pPager->zFilename, zMaster, pPager->dbSize)); |
/* If no database changes have been made, return early. */ |
if( pPager->eState<PAGER_WRITER_CACHEMOD ) return SQLITE_OK; |
- if( MEMDB ){ |
+ assert( MEMDB==0 || pPager->tempFile ); |
+ assert( isOpen(pPager->fd) || pPager->tempFile ); |
+ if( 0==pagerFlushOnCommit(pPager, 1) ){ |
/* If this is an in-memory db, or no pages have been written to, or this |
** function has already been called, it is mostly a no-op. However, any |
- ** backup in progress needs to be restarted. |
- */ |
+ ** backup in progress needs to be restarted. */ |
sqlite3BackupRestart(pPager->pBackup); |
}else{ |
if( pagerUseWal(pPager) ){ |
@@ -6412,6 +6539,7 @@ int sqlite3PagerRollback(Pager *pPager){ |
*/ |
pPager->errCode = SQLITE_ABORT; |
pPager->eState = PAGER_ERROR; |
+ setGetterMethod(pPager); |
return rc; |
} |
}else{ |
@@ -6512,10 +6640,10 @@ void sqlite3PagerCacheStat(Pager *pPager, int eStat, int reset, int *pnVal){ |
} |
/* |
-** Return true if this is an in-memory pager. |
+** Return true if this is an in-memory or temp-file backed pager. |
*/ |
int sqlite3PagerIsMemdb(Pager *pPager){ |
- return MEMDB; |
+ return pPager->tempFile; |
} |
/* |
@@ -6546,7 +6674,7 @@ static SQLITE_NOINLINE int pagerOpenSavepoint(Pager *pPager, int nSavepoint){ |
pPager->aSavepoint, sizeof(PagerSavepoint)*nSavepoint |
); |
if( !aNew ){ |
- return SQLITE_NOMEM; |
+ return SQLITE_NOMEM_BKPT; |
} |
memset(&aNew[nCurrent], 0, (nSavepoint-nCurrent) * sizeof(PagerSavepoint)); |
pPager->aSavepoint = aNew; |
@@ -6562,7 +6690,7 @@ static SQLITE_NOINLINE int pagerOpenSavepoint(Pager *pPager, int nSavepoint){ |
aNew[ii].iSubRec = pPager->nSubRec; |
aNew[ii].pInSavepoint = sqlite3BitvecCreate(pPager->dbSize); |
if( !aNew[ii].pInSavepoint ){ |
- return SQLITE_NOMEM; |
+ return SQLITE_NOMEM_BKPT; |
} |
if( pagerUseWal(pPager) ){ |
sqlite3WalSavepoint(pPager->pWal, aNew[ii].aWalData); |
@@ -6616,7 +6744,11 @@ int sqlite3PagerOpenSavepoint(Pager *pPager, int nSavepoint){ |
** savepoint. If no errors occur, SQLITE_OK is returned. |
*/ |
int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){ |
- int rc = pPager->errCode; /* Return code */ |
+ int rc = pPager->errCode; |
+ |
+#ifdef SQLITE_ENABLE_ZIPVFS |
+ if( op==SAVEPOINT_RELEASE ) rc = SQLITE_OK; |
+#endif |
assert( op==SAVEPOINT_RELEASE || op==SAVEPOINT_ROLLBACK ); |
assert( iSavepoint>=0 || op==SAVEPOINT_ROLLBACK ); |
@@ -6640,7 +6772,7 @@ int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){ |
if( op==SAVEPOINT_RELEASE ){ |
if( nNew==0 && isOpen(pPager->sjfd) ){ |
/* Only truncate if it is an in-memory sub-journal. */ |
- if( sqlite3IsMemJournal(pPager->sjfd) ){ |
+ if( sqlite3JournalIsInMemory(pPager->sjfd) ){ |
rc = sqlite3OsTruncate(pPager->sjfd, 0); |
assert( rc==SQLITE_OK ); |
} |
@@ -6657,6 +6789,21 @@ int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){ |
rc = pagerPlaybackSavepoint(pPager, pSavepoint); |
assert(rc!=SQLITE_DONE); |
} |
+ |
+#ifdef SQLITE_ENABLE_ZIPVFS |
+ /* If the cache has been modified but the savepoint cannot be rolled |
+ ** back journal_mode=off, put the pager in the error state. This way, |
+ ** if the VFS used by this pager includes ZipVFS, the entire transaction |
+ ** can be rolled back at the ZipVFS level. */ |
+ else if( |
+ pPager->journalMode==PAGER_JOURNALMODE_OFF |
+ && pPager->eState>=PAGER_WRITER_CACHEMOD |
+ ){ |
+ pPager->errCode = SQLITE_ABORT; |
+ pPager->eState = PAGER_ERROR; |
+ setGetterMethod(pPager); |
+ } |
+#endif |
} |
return rc; |
@@ -6711,14 +6858,6 @@ const char *sqlite3PagerJournalname(Pager *pPager){ |
return pPager->zJournal; |
} |
-/* |
-** Return true if fsync() calls are disabled for this pager. Return FALSE |
-** if fsync()s are executed normally. |
-*/ |
-int sqlite3PagerNosync(Pager *pPager){ |
- return pPager->noSync; |
-} |
- |
#ifdef SQLITE_HAS_CODEC |
/* |
** Set or retrieve the codec for this pager |
@@ -6735,6 +6874,7 @@ void sqlite3PagerSetCodec( |
pPager->xCodecSizeChng = xCodecSizeChng; |
pPager->xCodecFree = xCodecFree; |
pPager->pCodec = pCodec; |
+ setGetterMethod(pPager); |
pagerReportSize(pPager); |
} |
void *sqlite3PagerGetCodec(Pager *pPager){ |
@@ -6803,7 +6943,8 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){ |
/* In order to be able to rollback, an in-memory database must journal |
** the page we are moving from. |
*/ |
- if( MEMDB ){ |
+ assert( pPager->tempFile || !MEMDB ); |
+ if( pPager->tempFile ){ |
rc = sqlite3PagerWrite(pPg); |
if( rc ) return rc; |
} |
@@ -6860,7 +7001,7 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){ |
assert( !pPgOld || pPgOld->nRef==1 ); |
if( pPgOld ){ |
pPg->flags |= (pPgOld->flags&PGHDR_NEED_SYNC); |
- if( MEMDB ){ |
+ if( pPager->tempFile ){ |
/* Do not discard pages from an in-memory database since we might |
** need to rollback later. Just move the page out of the way. */ |
sqlite3PcacheMove(pPgOld, pPager->dbSize+1); |
@@ -6877,8 +7018,7 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){ |
** to exist, in case the transaction needs to roll back. Use pPgOld |
** as the original page since it has already been allocated. |
*/ |
- if( MEMDB ){ |
- assert( pPgOld ); |
+ if( pPager->tempFile && pPgOld ){ |
sqlite3PcacheMove(pPgOld, origPgno); |
sqlite3PagerUnrefNotNull(pPgOld); |
} |
@@ -7130,10 +7270,12 @@ sqlite3_backup **sqlite3PagerBackupPtr(Pager *pPager){ |
** Unless this is an in-memory or temporary database, clear the pager cache. |
*/ |
void sqlite3PagerClearCache(Pager *pPager){ |
- if( !MEMDB && pPager->tempFile==0 ) pager_reset(pPager); |
+ assert( MEMDB==0 || pPager->tempFile ); |
+ if( pPager->tempFile==0 ) pager_reset(pPager); |
} |
#endif |
+ |
#ifndef SQLITE_OMIT_WAL |
/* |
** This function is called when the user invokes "PRAGMA wal_checkpoint", |
@@ -7142,10 +7284,16 @@ void sqlite3PagerClearCache(Pager *pPager){ |
** |
** Parameter eMode is one of SQLITE_CHECKPOINT_PASSIVE, FULL or RESTART. |
*/ |
-int sqlite3PagerCheckpoint(Pager *pPager, int eMode, int *pnLog, int *pnCkpt){ |
+int sqlite3PagerCheckpoint( |
+ Pager *pPager, /* Checkpoint on this pager */ |
+ sqlite3 *db, /* Db handle used to check for interrupts */ |
+ int eMode, /* Type of checkpoint */ |
+ int *pnLog, /* OUT: Final number of frames in log */ |
+ int *pnCkpt /* OUT: Final number of checkpointed frames */ |
+){ |
int rc = SQLITE_OK; |
if( pPager->pWal ){ |
- rc = sqlite3WalCheckpoint(pPager->pWal, eMode, |
+ rc = sqlite3WalCheckpoint(pPager->pWal, db, eMode, |
(eMode==SQLITE_CHECKPOINT_PASSIVE ? 0 : pPager->xBusyHandler), |
pPager->pBusyHandlerArg, |
pPager->ckptSyncFlags, pPager->pageSize, (u8 *)pPager->pTmpSpace, |
@@ -7165,6 +7313,7 @@ int sqlite3PagerWalCallback(Pager *pPager){ |
*/ |
int sqlite3PagerWalSupported(Pager *pPager){ |
const sqlite3_io_methods *pMethods = pPager->fd->pMethods; |
+ if( pPager->noLock ) return 0; |
return pPager->exclusiveMode || (pMethods->iVersion>=2 && pMethods->xShmMap); |
} |
@@ -7276,7 +7425,7 @@ int sqlite3PagerOpenWal( |
** error (SQLITE_BUSY) is returned and the log connection is not closed. |
** If successful, the EXCLUSIVE lock is not released before returning. |
*/ |
-int sqlite3PagerCloseWal(Pager *pPager){ |
+int sqlite3PagerCloseWal(Pager *pPager, sqlite3 *db){ |
int rc = SQLITE_OK; |
assert( pPager->journalMode==PAGER_JOURNALMODE_WAL ); |
@@ -7304,10 +7453,11 @@ int sqlite3PagerCloseWal(Pager *pPager){ |
if( rc==SQLITE_OK && pPager->pWal ){ |
rc = pagerExclusiveLock(pPager); |
if( rc==SQLITE_OK ){ |
- rc = sqlite3WalClose(pPager->pWal, pPager->ckptSyncFlags, |
+ rc = sqlite3WalClose(pPager->pWal, db, pPager->ckptSyncFlags, |
pPager->pageSize, (u8*)pPager->pTmpSpace); |
pPager->pWal = 0; |
pagerFixMaplimit(pPager); |
+ if( rc && !pPager->exclusiveMode ) pagerUnlockDb(pPager, SHARED_LOCK); |
} |
} |
return rc; |
@@ -7340,6 +7490,20 @@ int sqlite3PagerSnapshotOpen(Pager *pPager, sqlite3_snapshot *pSnapshot){ |
} |
return rc; |
} |
+ |
+/* |
+** If this is a WAL database, call sqlite3WalSnapshotRecover(). If this |
+** is not a WAL database, return an error. |
+*/ |
+int sqlite3PagerSnapshotRecover(Pager *pPager){ |
+ int rc; |
+ if( pPager->pWal ){ |
+ rc = sqlite3WalSnapshotRecover(pPager->pWal); |
+ }else{ |
+ rc = SQLITE_ERROR; |
+ } |
+ return rc; |
+} |
#endif /* SQLITE_ENABLE_SNAPSHOT */ |
#endif /* !SQLITE_OMIT_WAL */ |
@@ -7357,5 +7521,4 @@ int sqlite3PagerWalFramesize(Pager *pPager){ |
} |
#endif |
- |
#endif /* SQLITE_OMIT_DISKIO */ |