| 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 */
|
|
|