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 a4fe31869376f59978bf7d9c5a90ed7db8409576..34fa50fe8cf582eb2a9a26b0837414aaad345cee 100644 |
--- a/third_party/sqlite/src/src/pager.c |
+++ b/third_party/sqlite/src/src/pager.c |
@@ -75,13 +75,13 @@ |
** |
** Definition: Two databases (or the same database at two points it time) |
** are said to be "logically equivalent" if they give the same answer to |
-** all queries. Note in particular the the content of freelist leaf |
-** pages can be changed arbitarily without effecting the logical equivalence |
+** all queries. Note in particular the content of freelist leaf |
+** pages can be changed arbitrarily without affecting the logical equivalence |
** of the database. |
** |
** (7) At any time, if any subset, including the empty set and the total set, |
** of the unsynced changes to a rollback journal are removed and the |
-** journal is rolled back, the resulting database file will be logical |
+** journal is rolled back, the resulting database file will be logically |
** equivalent to the database file at the beginning of the transaction. |
** |
** (8) When a transaction is rolled back, the xTruncate method of the VFS |
@@ -273,7 +273,7 @@ int sqlite3PagerTrace=1; /* True to enable tracing */ |
** * A write transaction is active. |
** * An EXCLUSIVE or greater lock is held on the database file. |
** * All writing and syncing of journal and database data has finished. |
-** If no error occured, all that remains is to finalize the journal to |
+** If no error occurred, all that remains is to finalize the journal to |
** commit the transaction. If an error did occur, the caller will need |
** to rollback the transaction. |
** |
@@ -378,7 +378,7 @@ int sqlite3PagerTrace=1; /* True to enable tracing */ |
** |
** The exception is when the database file is unlocked as the pager moves |
** from ERROR to OPEN state. At this point there may be a hot-journal file |
-** in the file-system that needs to be rolled back (as part of a OPEN->SHARED |
+** in the file-system that needs to be rolled back (as part of an OPEN->SHARED |
** transition, by the same pager or any other). If the call to xUnlock() |
** fails at this point and the pager is left holding an EXCLUSIVE lock, this |
** can confuse the call to xCheckReservedLock() call made later as part |
@@ -454,7 +454,14 @@ struct PagerSavepoint { |
}; |
/* |
-** A open page cache is an instance of struct Pager. A description of |
+** Bits of the Pager.doNotSpill flag. See further description below. |
+*/ |
+#define SPILLFLAG_OFF 0x01 /* Never spill cache. Set via pragma */ |
+#define SPILLFLAG_ROLLBACK 0x02 /* Current rolling back, so do not spill */ |
+#define SPILLFLAG_NOSYNC 0x04 /* Spill is ok, but do not sync */ |
+ |
+/* |
+** An open page cache is an instance of struct Pager. A description of |
** some of the more important member variables follows: |
** |
** eState |
@@ -519,19 +526,21 @@ struct PagerSavepoint { |
** journal file from being successfully finalized, the setMaster flag |
** is cleared anyway (and the pager will move to ERROR state). |
** |
-** doNotSpill, doNotSyncSpill |
+** doNotSpill |
** |
-** These two boolean variables control the behaviour of cache-spills |
-** (calls made by the pcache module to the pagerStress() routine to |
-** write cached data to the file-system in order to free up memory). |
+** This variables control the behavior of cache-spills (calls made by |
+** the pcache module to the pagerStress() routine to write cached data |
+** to the file-system in order to free up memory). |
** |
-** When doNotSpill is non-zero, writing to the database from pagerStress() |
-** is disabled altogether. This is done in a very obscure case that |
+** When bits SPILLFLAG_OFF or SPILLFLAG_ROLLBACK of doNotSpill are set, |
+** writing to the database from pagerStress() is disabled altogether. |
+** The SPILLFLAG_ROLLBACK case is done in a very obscure case that |
** comes up during savepoint rollback that requires the pcache module |
** to allocate a new page to prevent the journal file from being written |
-** while it is being traversed by code in pager_playback(). |
+** while it is being traversed by code in pager_playback(). The SPILLFLAG_OFF |
+** case is a user preference. |
** |
-** If doNotSyncSpill is non-zero, writing to the database from pagerStress() |
+** If the SPILLFLAG_NOSYNC bit is set, writing to the database from pagerStress() |
** is permitted, but syncing the journal file is not. This flag is set |
** by sqlite3PagerWrite() when the file-system sector-size is larger than |
** the database page-size in order to prevent a journal sync from happening |
@@ -612,18 +621,19 @@ struct Pager { |
u8 exclusiveMode; /* Boolean. True if locking_mode==EXCLUSIVE */ |
u8 journalMode; /* One of the PAGER_JOURNALMODE_* values */ |
u8 useJournal; /* Use a rollback journal on this file */ |
- u8 noReadlock; /* Do not bother to obtain readlocks */ |
u8 noSync; /* Do not sync the journal if true */ |
u8 fullSync; /* Do extra syncs of the journal for robustness */ |
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 */ |
- u8 tempFile; /* zFilename is a temporary file */ |
+ u8 tempFile; /* zFilename is a temporary or immutable file */ |
+ u8 noLock; /* Do not lock (except in WAL mode) */ |
u8 readOnly; /* True for a read-only database */ |
u8 memDb; /* True to inhibit all file I/O */ |
/************************************************************************** |
** The following block contains those class members that change during |
- ** routine opertion. Class members not in this block are either fixed |
+ ** routine operation. Class members not in this block are either fixed |
** when the pager is first created or else only change when there is a |
** significant mode change (such as changing the page_size, locking_mode, |
** or the journal_mode). From another view, these class members describe |
@@ -635,7 +645,6 @@ struct Pager { |
u8 changeCountDone; /* Set after incrementing the change-counter */ |
u8 setMaster; /* True if a m-j name has been written to jrnl */ |
u8 doNotSpill; /* Do not spill the cache when non-zero */ |
- u8 doNotSyncSpill; /* Do not do a spill that requires jrnl sync */ |
u8 subjInMemory; /* True to use in-memory sub-journals */ |
Pgno dbSize; /* Number of pages in the database */ |
Pgno dbOrigSize; /* dbSize before the current transaction */ |
@@ -655,6 +664,11 @@ struct Pager { |
PagerSavepoint *aSavepoint; /* Array of active savepoints */ |
int nSavepoint; /* Number of elements in aSavepoint[] */ |
char dbFileVers[16]; /* Changes whenever database file changes */ |
+ |
+ u8 bUseFetch; /* True to use xFetch() */ |
+ int nMmapOut; /* Number of mmap pages currently outstanding */ |
+ sqlite3_int64 szMmap; /* Desired maximum mmap size */ |
+ PgHdr *pMmapFreelist; /* List of free mmap page headers (pDirty) */ |
/* |
** End of the routinely-changing class members |
***************************************************************************/ |
@@ -670,9 +684,9 @@ struct Pager { |
char *zJournal; /* Name of the journal file */ |
int (*xBusyHandler)(void*); /* Function to call when busy */ |
void *pBusyHandlerArg; /* Context argument for xBusyHandler */ |
+ int aStat[3]; /* Total cache hits, misses and writes */ |
#ifdef SQLITE_TEST |
- int nHit, nMiss; /* Cache hits and missing */ |
- int nRead, nWrite; /* Database pages read/written */ |
+ int nRead; /* Database pages read */ |
#endif |
void (*xReiniter)(DbPage*); /* Call this routine when reloading pages */ |
#ifdef SQLITE_HAS_CODEC |
@@ -690,6 +704,15 @@ struct Pager { |
}; |
/* |
+** Indexes for use with Pager.aStat[]. The Pager.aStat[] array contains |
+** the values accessed by passing SQLITE_DBSTATUS_CACHE_HIT, CACHE_MISS |
+** or CACHE_WRITE to sqlite3_db_status(). |
+*/ |
+#define PAGER_STAT_HIT 0 |
+#define PAGER_STAT_MISS 1 |
+#define PAGER_STAT_WRITE 2 |
+ |
+/* |
** The following global variables hold counters used for |
** testing purposes only. These variables do not exist in |
** a non-testing build. These variables are not thread-safe. |
@@ -757,6 +780,16 @@ static const unsigned char aJournalMagic[] = { |
#endif |
/* |
+** The macro USEFETCH is true if we are allowed to use the xFetch and xUnfetch |
+** interfaces to access the database using memory-mapped I/O. |
+*/ |
+#if SQLITE_MAX_MMAP_SIZE>0 |
+# define USEFETCH(x) ((x)->bUseFetch) |
+#else |
+# define USEFETCH(x) 0 |
+#endif |
+ |
+/* |
** The maximum legal page number is (2^31 - 1). |
*/ |
#define PAGER_MAX_PGNO 2147483647 |
@@ -786,7 +819,7 @@ static int pagerUseWal(Pager *pPager){ |
#else |
# define pagerUseWal(x) 0 |
# define pagerRollbackWal(x) 0 |
-# define pagerWalFrames(v,w,x,y,z) 0 |
+# define pagerWalFrames(v,w,x,y) 0 |
# define pagerOpenWalIfPresent(z) SQLITE_OK |
# define pagerBeginReadTransaction(z) SQLITE_OK |
#endif |
@@ -859,7 +892,7 @@ static int assert_pager_state(Pager *p){ |
case PAGER_READER: |
assert( pPager->errCode==SQLITE_OK ); |
assert( p->eLock!=UNKNOWN_LOCK ); |
- assert( p->eLock>=SHARED_LOCK || p->noReadlock ); |
+ assert( p->eLock>=SHARED_LOCK ); |
break; |
case PAGER_WRITER_LOCKED: |
@@ -990,11 +1023,12 @@ static char *print_pager_state(Pager *p){ |
** PagerSavepoint.pInSavepoint. |
*/ |
static int subjRequiresPage(PgHdr *pPg){ |
- Pgno pgno = pPg->pgno; |
Pager *pPager = pPg->pPager; |
+ PagerSavepoint *p; |
+ Pgno pgno = pPg->pgno; |
int i; |
for(i=0; i<pPager->nSavepoint; i++){ |
- PagerSavepoint *p = &pPager->aSavepoint[i]; |
+ p = &pPager->aSavepoint[i]; |
if( p->nOrig>=pgno && 0==sqlite3BitvecTest(p->pInSavepoint, pgno) ){ |
return 1; |
} |
@@ -1005,8 +1039,8 @@ static int subjRequiresPage(PgHdr *pPg){ |
/* |
** Return true if the page is already in the journal file. |
*/ |
-static int pageInJournal(PgHdr *pPg){ |
- return sqlite3BitvecTest(pPg->pPager->pInJournal, pPg->pgno); |
+static int pageInJournal(Pager *pPager, PgHdr *pPg){ |
+ return sqlite3BitvecTest(pPager->pInJournal, pPg->pgno); |
} |
/* |
@@ -1058,7 +1092,7 @@ static int pagerUnlockDb(Pager *pPager, int eLock){ |
assert( eLock!=NO_LOCK || pagerUseWal(pPager)==0 ); |
if( isOpen(pPager->fd) ){ |
assert( pPager->eLock>=eLock ); |
- rc = sqlite3OsUnlock(pPager->fd, eLock); |
+ rc = pPager->noLock ? SQLITE_OK : sqlite3OsUnlock(pPager->fd, eLock); |
if( pPager->eLock!=UNKNOWN_LOCK ){ |
pPager->eLock = (u8)eLock; |
} |
@@ -1082,7 +1116,7 @@ static int pagerLockDb(Pager *pPager, int eLock){ |
assert( eLock==SHARED_LOCK || eLock==RESERVED_LOCK || eLock==EXCLUSIVE_LOCK ); |
if( pPager->eLock<eLock || pPager->eLock==UNKNOWN_LOCK ){ |
- rc = sqlite3OsLock(pPager->fd, eLock); |
+ rc = pPager->noLock ? SQLITE_OK : sqlite3OsLock(pPager->fd, eLock); |
if( rc==SQLITE_OK && (pPager->eLock!=UNKNOWN_LOCK||eLock==EXCLUSIVE_LOCK) ){ |
pPager->eLock = (u8)eLock; |
IOTRACE(("LOCK %p %d\n", pPager, eLock)) |
@@ -1213,6 +1247,7 @@ static int readMasterJournal(sqlite3_file *pJrnl, char *zMaster, u32 nMaster){ |
|| szJ<16 |
|| SQLITE_OK!=(rc = read32bits(pJrnl, szJ-16, &len)) |
|| len>=nMaster |
+ || len==0 |
|| SQLITE_OK!=(rc = read32bits(pJrnl, szJ-12, &cksum)) |
|| SQLITE_OK!=(rc = sqlite3OsRead(pJrnl, aMagic, 8, szJ-8)) |
|| memcmp(aMagic, aJournalMagic, 8) |
@@ -1390,7 +1425,7 @@ static int writeJournalHdr(Pager *pPager){ |
memset(zHeader, 0, sizeof(aJournalMagic)+4); |
} |
- /* The random check-hash initialiser */ |
+ /* The random check-hash initializer */ |
sqlite3_randomness(sizeof(pPager->cksumInit), &pPager->cksumInit); |
put32bits(&zHeader[sizeof(aJournalMagic)+4], pPager->cksumInit); |
/* The initial database size */ |
@@ -1590,12 +1625,11 @@ static int writeMasterJournal(Pager *pPager, const char *zMaster){ |
if( !zMaster |
|| pPager->journalMode==PAGER_JOURNALMODE_MEMORY |
- || pPager->journalMode==PAGER_JOURNALMODE_OFF |
+ || !isOpen(pPager->jfd) |
){ |
return SQLITE_OK; |
} |
pPager->setMaster = 1; |
- assert( isOpen(pPager->jfd) ); |
assert( pPager->journalHdr <= pPager->journalOff ); |
/* Calculate the length in bytes and the checksum of zMaster */ |
@@ -1644,21 +1678,6 @@ static int writeMasterJournal(Pager *pPager, const char *zMaster){ |
} |
/* |
-** Find a page in the hash table given its page number. Return |
-** a pointer to the page or NULL if the requested page is not |
-** already in memory. |
-*/ |
-static PgHdr *pager_lookup(Pager *pPager, Pgno pgno){ |
- PgHdr *p; /* Return value */ |
- |
- /* It is not possible for a call to PcacheFetch() with createFlag==0 to |
- ** fail, since no attempt to allocate dynamic memory will be made. |
- */ |
- (void)sqlite3PcacheFetch(pPager->pPCache, pgno, 0, &p); |
- return p; |
-} |
- |
-/* |
** Discard the entire contents of the in-memory page-cache. |
*/ |
static void pager_reset(Pager *pPager){ |
@@ -1788,6 +1807,7 @@ static void pager_unlock(Pager *pPager){ |
pPager->changeCountDone = pPager->tempFile; |
pPager->eState = PAGER_OPEN; |
pPager->errCode = SQLITE_OK; |
+ if( USEFETCH(pPager) ) sqlite3OsUnfetch(pPager->fd, 0, 0); |
} |
pPager->journalOff = 0; |
@@ -1829,6 +1849,8 @@ static int pager_error(Pager *pPager, int rc){ |
return rc; |
} |
+static int pager_truncate(Pager *pPager, Pgno nPage); |
+ |
/* |
** This routine ends a transaction. A transaction is usually ended by |
** either a COMMIT or a ROLLBACK operation. This routine may be called |
@@ -1882,7 +1904,7 @@ static int pager_error(Pager *pPager, int rc){ |
** to the first error encountered (the journal finalization one) is |
** returned. |
*/ |
-static int pager_end_transaction(Pager *pPager, int hasMaster){ |
+static int pager_end_transaction(Pager *pPager, int hasMaster, int bCommit){ |
int rc = SQLITE_OK; /* Error code from journal finalization operation */ |
int rc2 = SQLITE_OK; /* Error code from db file unlock operation */ |
@@ -1919,6 +1941,14 @@ static int pager_end_transaction(Pager *pPager, int hasMaster){ |
rc = SQLITE_OK; |
}else{ |
rc = sqlite3OsTruncate(pPager->jfd, 0); |
+ if( rc==SQLITE_OK && pPager->fullSync ){ |
+ /* Make sure the new file size is written into the inode right away. |
+ ** Otherwise the journal might resurrect following a power loss and |
+ ** cause the last transaction to roll back. See |
+ ** https://bugzilla.mozilla.org/show_bug.cgi?id=1072773 |
+ */ |
+ rc = sqlite3OsSync(pPager->jfd, pPager->syncFlags); |
+ } |
} |
pPager->journalOff = 0; |
}else if( pPager->journalMode==PAGER_JOURNALMODE_PERSIST |
@@ -1932,12 +1962,13 @@ static int pager_end_transaction(Pager *pPager, int hasMaster){ |
** file should be closed and deleted. If this connection writes to |
** the database file, it will do so using an in-memory journal. |
*/ |
+ int bDelete = (!pPager->tempFile && sqlite3JournalExists(pPager->jfd)); |
assert( pPager->journalMode==PAGER_JOURNALMODE_DELETE |
|| pPager->journalMode==PAGER_JOURNALMODE_MEMORY |
|| pPager->journalMode==PAGER_JOURNALMODE_WAL |
); |
sqlite3OsClose(pPager->jfd); |
- if( !pPager->tempFile ){ |
+ if( bDelete ){ |
rc = sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0); |
} |
} |
@@ -1946,10 +1977,10 @@ static int pager_end_transaction(Pager *pPager, int hasMaster){ |
#ifdef SQLITE_CHECK_PAGES |
sqlite3PcacheIterateDirty(pPager->pPCache, pager_set_pagehash); |
if( pPager->dbSize==0 && sqlite3PcacheRefCount(pPager->pPCache)>0 ){ |
- PgHdr *p = pager_lookup(pPager, 1); |
+ PgHdr *p = sqlite3PagerLookup(pPager, 1); |
if( p ){ |
p->pageHash = 0; |
- sqlite3PagerUnref(p); |
+ sqlite3PagerUnrefNotNull(p); |
} |
} |
#endif |
@@ -1967,7 +1998,22 @@ static int pager_end_transaction(Pager *pPager, int hasMaster){ |
*/ |
rc2 = sqlite3WalEndWriteTransaction(pPager->pWal); |
assert( rc2==SQLITE_OK ); |
+ }else if( rc==SQLITE_OK && bCommit && pPager->dbFileSize>pPager->dbSize ){ |
+ /* This branch is taken when committing a transaction in rollback-journal |
+ ** mode if the database file on disk is larger than the database image. |
+ ** At this point the journal has been finalized and the transaction |
+ ** successfully committed, but the EXCLUSIVE lock is still held on the |
+ ** file. So it is safe to truncate the database file to its minimum |
+ ** required size. */ |
+ assert( pPager->eLock==EXCLUSIVE_LOCK ); |
+ rc = pager_truncate(pPager, pPager->dbSize); |
+ } |
+ |
+ if( rc==SQLITE_OK && bCommit && isOpen(pPager->fd) ){ |
+ rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_COMMIT_PHASETWO, 0); |
+ if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK; |
} |
+ |
if( !pPager->exclusiveMode |
&& (!pagerUseWal(pPager) || sqlite3WalExclusiveMode(pPager->pWal, 0)) |
){ |
@@ -2006,7 +2052,7 @@ static void pagerUnlockAndRollback(Pager *pPager){ |
sqlite3EndBenignMalloc(); |
}else if( !pPager->exclusiveMode ){ |
assert( pPager->eState==PAGER_READER ); |
- pager_end_transaction(pPager, 0); |
+ pager_end_transaction(pPager, 0, 0); |
} |
} |
pager_unlock(pPager); |
@@ -2210,7 +2256,7 @@ static int pager_playback_one_page( |
if( pagerUseWal(pPager) ){ |
pPg = 0; |
}else{ |
- pPg = pager_lookup(pPager, pgno); |
+ pPg = sqlite3PagerLookup(pPager, pgno); |
} |
assert( pPg || !MEMDB ); |
assert( pPager->eState!=PAGER_OPEN || pPg==0 ); |
@@ -2230,7 +2276,7 @@ static int pager_playback_one_page( |
i64 ofst = (pgno-1)*(i64)pPager->pageSize; |
testcase( !isSavepnt && pPg!=0 && (pPg->flags&PGHDR_NEED_SYNC)!=0 ); |
assert( !pagerUseWal(pPager) ); |
- rc = sqlite3OsWrite(pPager->fd, (u8*)aData, pPager->pageSize, ofst); |
+ rc = sqlite3OsWrite(pPager->fd, (u8 *)aData, pPager->pageSize, ofst); |
if( pgno>pPager->dbFileSize ){ |
pPager->dbFileSize = pgno; |
} |
@@ -2257,11 +2303,11 @@ static int pager_playback_one_page( |
** requiring a journal-sync before it is written. |
*/ |
assert( isSavepnt ); |
- assert( pPager->doNotSpill==0 ); |
- pPager->doNotSpill++; |
+ assert( (pPager->doNotSpill & SPILLFLAG_ROLLBACK)==0 ); |
+ pPager->doNotSpill |= SPILLFLAG_ROLLBACK; |
rc = sqlite3PagerAcquire(pPager, pgno, &pPg, 1); |
- assert( pPager->doNotSpill==1 ); |
- pPager->doNotSpill--; |
+ assert( (pPager->doNotSpill & SPILLFLAG_ROLLBACK)!=0 ); |
+ pPager->doNotSpill &= ~SPILLFLAG_ROLLBACK; |
if( rc!=SQLITE_OK ) return rc; |
pPg->flags &= ~PGHDR_NEED_READ; |
sqlite3PcacheMakeDirty(pPg); |
@@ -2390,7 +2436,7 @@ static int pager_delmaster(Pager *pPager, const char *zMaster){ |
rc = sqlite3OsFileSize(pMaster, &nMasterJournal); |
if( rc!=SQLITE_OK ) goto delmaster_out; |
nMasterPtr = pVfs->mxPathname+1; |
- zMasterJournal = sqlite3Malloc((int)nMasterJournal + nMasterPtr + 1); |
+ zMasterJournal = sqlite3Malloc(nMasterJournal + nMasterPtr + 1); |
if( !zMasterJournal ){ |
rc = SQLITE_NOMEM; |
goto delmaster_out; |
@@ -2459,7 +2505,7 @@ delmaster_out: |
** If the file on disk is currently larger than nPage pages, then use the VFS |
** xTruncate() method to truncate it. |
** |
-** Or, it might might be the case that the file on disk is smaller than |
+** Or, it might be the case that the file on disk is smaller than |
** nPage pages. Some operating system implementations can get confused if |
** you try to truncate a file to some size that is larger than it |
** currently is, so detect this case and write a single zero byte to |
@@ -2485,10 +2531,9 @@ static int pager_truncate(Pager *pPager, Pgno nPage){ |
if( rc==SQLITE_OK && currentSize!=newSize ){ |
if( currentSize>newSize ){ |
rc = sqlite3OsTruncate(pPager->fd, newSize); |
- }else{ |
+ }else if( (currentSize+szPage)<=newSize ){ |
char *pTmp = pPager->pTmpSpace; |
memset(pTmp, 0, szPage); |
- testcase( (newSize-szPage) < currentSize ); |
testcase( (newSize-szPage) == currentSize ); |
testcase( (newSize-szPage) > currentSize ); |
rc = sqlite3OsWrite(pPager->fd, pTmp, szPage, newSize-szPage); |
@@ -2502,9 +2547,24 @@ static int pager_truncate(Pager *pPager, Pgno nPage){ |
} |
/* |
+** Return a sanitized version of the sector-size of OS file pFile. The |
+** return value is guaranteed to lie between 32 and MAX_SECTOR_SIZE. |
+*/ |
+int sqlite3SectorSize(sqlite3_file *pFile){ |
+ int iRet = sqlite3OsSectorSize(pFile); |
+ if( iRet<32 ){ |
+ iRet = 512; |
+ }else if( iRet>MAX_SECTOR_SIZE ){ |
+ assert( MAX_SECTOR_SIZE>=512 ); |
+ iRet = MAX_SECTOR_SIZE; |
+ } |
+ return iRet; |
+} |
+ |
+/* |
** Set the value of the Pager.sectorSize variable for the given |
** pager based on the value returned by the xSectorSize method |
-** of the open database file. The sector size will be used used |
+** of the open database file. The sector size will be used |
** to determine the size and alignment of journal header and |
** master journal pointers within created journal files. |
** |
@@ -2514,23 +2574,29 @@ static int pager_truncate(Pager *pPager, Pgno nPage){ |
** the value returned by the xSectorSize() method rounded up to 32 if |
** it is less than 32, or rounded down to MAX_SECTOR_SIZE if it |
** is greater than MAX_SECTOR_SIZE. |
+** |
+** If the file has the SQLITE_IOCAP_POWERSAFE_OVERWRITE property, then set |
+** the effective sector size to its minimum value (512). The purpose of |
+** pPager->sectorSize is to define the "blast radius" of bytes that |
+** might change if a crash occurs while writing to a single byte in |
+** that range. But with POWERSAFE_OVERWRITE, the blast radius is zero |
+** (that is what POWERSAFE_OVERWRITE means), so we minimize the sector |
+** size. For backwards compatibility of the rollback journal file format, |
+** we cannot reduce the effective sector size below 512. |
*/ |
static void setSectorSize(Pager *pPager){ |
assert( isOpen(pPager->fd) || pPager->tempFile ); |
- if( !pPager->tempFile ){ |
+ if( pPager->tempFile |
+ || (sqlite3OsDeviceCharacteristics(pPager->fd) & |
+ SQLITE_IOCAP_POWERSAFE_OVERWRITE)!=0 |
+ ){ |
/* Sector size doesn't matter for temporary files. Also, the file |
** may not have been opened yet, in which case the OsSectorSize() |
- ** call will segfault. |
- */ |
- pPager->sectorSize = sqlite3OsSectorSize(pPager->fd); |
- } |
- if( pPager->sectorSize<32 ){ |
+ ** call will segfault. */ |
pPager->sectorSize = 512; |
- } |
- if( pPager->sectorSize>MAX_SECTOR_SIZE ){ |
- assert( MAX_SECTOR_SIZE>=512 ); |
- pPager->sectorSize = MAX_SECTOR_SIZE; |
+ }else{ |
+ pPager->sectorSize = sqlite3SectorSize(pPager->fd); |
} |
} |
@@ -2601,6 +2667,7 @@ static int pager_playback(Pager *pPager, int isHot){ |
int res = 1; /* Value returned by sqlite3OsAccess() */ |
char *zMaster = 0; /* Name of master journal file if any */ |
int needPagerReset; /* True to reset page prior to first page rollback */ |
+ int nPlayback = 0; /* Total number of pages restored from journal */ |
/* Figure out how many records are in the journal. Abort early if |
** the journal is empty. |
@@ -2701,9 +2768,10 @@ static int pager_playback(Pager *pPager, int isHot){ |
needPagerReset = 0; |
} |
rc = pager_playback_one_page(pPager,&pPager->journalOff,0,1,0); |
- if( rc!=SQLITE_OK ){ |
+ if( rc==SQLITE_OK ){ |
+ nPlayback++; |
+ }else{ |
if( rc==SQLITE_DONE ){ |
- rc = SQLITE_OK; |
pPager->journalOff = szJ; |
break; |
}else if( rc==SQLITE_IOERR_SHORT_READ ){ |
@@ -2734,10 +2802,11 @@ end_playback: |
** SQLITE_FCNTL_DB_UNCHANGED file-control method to disable the |
** assertion that the transaction counter was modified. |
*/ |
- assert( |
- pPager->fd->pMethods==0 || |
- sqlite3OsFileControl(pPager->fd,SQLITE_FCNTL_DB_UNCHANGED,0)>=SQLITE_OK |
- ); |
+#ifdef SQLITE_DEBUG |
+ if( pPager->fd->pMethods ){ |
+ sqlite3OsFileControlHint(pPager->fd,SQLITE_FCNTL_DB_UNCHANGED,0); |
+ } |
+#endif |
/* If this playback is happening automatically as a result of an IO or |
** malloc error that occurred after the change-counter was updated but |
@@ -2758,10 +2827,10 @@ end_playback: |
if( rc==SQLITE_OK |
&& (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN) |
){ |
- rc = sqlite3PagerSync(pPager); |
+ rc = sqlite3PagerSync(pPager, 0); |
} |
if( rc==SQLITE_OK ){ |
- rc = pager_end_transaction(pPager, zMaster[0]!='\0'); |
+ rc = pager_end_transaction(pPager, zMaster[0]!='\0', 0); |
testcase( rc!=SQLITE_OK ); |
} |
if( rc==SQLITE_OK && zMaster[0] && res ){ |
@@ -2771,6 +2840,10 @@ end_playback: |
rc = pager_delmaster(pPager, zMaster); |
testcase( rc!=SQLITE_OK ); |
} |
+ if( isHot && nPlayback ){ |
+ sqlite3_log(SQLITE_NOTICE_RECOVER_ROLLBACK, "recovered %d pages from %s", |
+ nPlayback, pPager->zJournal); |
+ } |
/* The Pager.sectorSize variable may have been updated while rolling |
** back a journal created by a process with a different sector size |
@@ -2792,27 +2865,22 @@ end_playback: |
** If an IO error occurs, then the IO error is returned to the caller. |
** Otherwise, SQLITE_OK is returned. |
*/ |
-static int readDbPage(PgHdr *pPg){ |
+static int readDbPage(PgHdr *pPg, u32 iFrame){ |
Pager *pPager = pPg->pPager; /* Pager object associated with page pPg */ |
Pgno pgno = pPg->pgno; /* Page number to read */ |
int rc = SQLITE_OK; /* Return code */ |
- int isInWal = 0; /* True if page is in log file */ |
int pgsz = pPager->pageSize; /* Number of bytes to read */ |
assert( pPager->eState>=PAGER_READER && !MEMDB ); |
assert( isOpen(pPager->fd) ); |
- if( NEVER(!isOpen(pPager->fd)) ){ |
- assert( pPager->tempFile ); |
- memset(pPg->pData, 0, pPager->pageSize); |
- return SQLITE_OK; |
- } |
- |
- if( pagerUseWal(pPager) ){ |
+#ifndef SQLITE_OMIT_WAL |
+ if( iFrame ){ |
/* Try to pull the page from the write-ahead log. */ |
- rc = sqlite3WalRead(pPager->pWal, pgno, &isInWal, pgsz, pPg->pData); |
- } |
- if( rc==SQLITE_OK && !isInWal ){ |
+ rc = sqlite3WalReadFrame(pPager->pWal, iFrame, pgsz, pPg->pData); |
+ }else |
+#endif |
+ { |
i64 iOffset = (pgno-1)*(i64)pPager->pageSize; |
rc = sqlite3OsRead(pPager->fd, pPg->pData, pgsz, iOffset); |
if( rc==SQLITE_IOERR_SHORT_READ ){ |
@@ -2891,16 +2959,21 @@ static int pagerUndoCallback(void *pCtx, Pgno iPg){ |
Pager *pPager = (Pager *)pCtx; |
PgHdr *pPg; |
+ assert( pagerUseWal(pPager) ); |
pPg = sqlite3PagerLookup(pPager, iPg); |
if( pPg ){ |
if( sqlite3PcachePageRefcount(pPg)==1 ){ |
sqlite3PcacheDrop(pPg); |
}else{ |
- rc = readDbPage(pPg); |
+ u32 iFrame = 0; |
+ rc = sqlite3WalFindFrame(pPager->pWal, pPg->pgno, &iFrame); |
+ if( rc==SQLITE_OK ){ |
+ rc = readDbPage(pPg, iFrame); |
+ } |
if( rc==SQLITE_OK ){ |
pPager->xReiniter(pPg); |
} |
- sqlite3PagerUnref(pPg); |
+ sqlite3PagerUnrefNotNull(pPg); |
} |
} |
@@ -2956,15 +3029,16 @@ static int pagerWalFrames( |
Pager *pPager, /* Pager object */ |
PgHdr *pList, /* List of frames to log */ |
Pgno nTruncate, /* Database size after this commit */ |
- int isCommit, /* True if this is a commit */ |
- int syncFlags /* Flags to pass to OsSync() (or 0) */ |
+ int isCommit /* True if this is a commit */ |
){ |
int rc; /* Return code */ |
+ int nList; /* Number of pages in pList */ |
#if defined(SQLITE_DEBUG) || defined(SQLITE_CHECK_PAGES) |
PgHdr *p; /* For looping over pages */ |
#endif |
assert( pPager->pWal ); |
+ assert( pList ); |
#ifdef SQLITE_DEBUG |
/* Verify that the page list is in accending order */ |
for(p=pList; p && p->pDirty; p=p->pDirty){ |
@@ -2972,6 +3046,7 @@ static int pagerWalFrames( |
} |
#endif |
+ assert( pList->pDirty==0 || isCommit ); |
if( isCommit ){ |
/* If a WAL transaction is being committed, there is no point in writing |
** any pages with page numbers greater than nTruncate into the WAL file. |
@@ -2979,15 +3054,22 @@ static int pagerWalFrames( |
** list here. */ |
PgHdr *p; |
PgHdr **ppNext = &pList; |
- for(p=pList; (*ppNext = p); p=p->pDirty){ |
- if( p->pgno<=nTruncate ) ppNext = &p->pDirty; |
+ nList = 0; |
+ for(p=pList; (*ppNext = p)!=0; p=p->pDirty){ |
+ if( p->pgno<=nTruncate ){ |
+ ppNext = &p->pDirty; |
+ nList++; |
+ } |
} |
assert( pList ); |
+ }else{ |
+ nList = 1; |
} |
+ pPager->aStat[PAGER_STAT_WRITE] += nList; |
if( pList->pgno==1 ) pager_write_changecounter(pList); |
rc = sqlite3WalFrames(pPager->pWal, |
- pPager->pageSize, pList, nTruncate, isCommit, syncFlags |
+ pPager->pageSize, pList, nTruncate, isCommit, pPager->walSyncFlags |
); |
if( rc==SQLITE_OK && pPager->pBackup ){ |
PgHdr *p; |
@@ -3031,6 +3113,7 @@ static int pagerBeginReadTransaction(Pager *pPager){ |
rc = sqlite3WalBeginReadTransaction(pPager->pWal, &changed); |
if( rc!=SQLITE_OK || changed ){ |
pager_reset(pPager); |
+ if( USEFETCH(pPager) ) sqlite3OsUnfetch(pPager->fd, 0, 0); |
} |
return rc; |
@@ -3056,7 +3139,7 @@ static int pagerPagecount(Pager *pPager, Pgno *pnPage){ |
** contains no valid committed transactions. |
*/ |
assert( pPager->eState==PAGER_OPEN ); |
- assert( pPager->eLock>=SHARED_LOCK || pPager->noReadlock ); |
+ assert( pPager->eLock>=SHARED_LOCK ); |
nPage = sqlite3WalDbsize(pPager->pWal); |
/* If the database size was not available from the WAL sub-system, |
@@ -3074,10 +3157,7 @@ static int pagerPagecount(Pager *pPager, Pgno *pnPage){ |
return rc; |
} |
} |
- nPage = (Pgno)(n / pPager->pageSize); |
- if( nPage==0 && n>0 ){ |
- nPage = 1; |
- } |
+ nPage = (Pgno)((n+pPager->pageSize-1) / pPager->pageSize); |
} |
/* If the current number of pages in the file is greater than the |
@@ -3114,7 +3194,7 @@ static int pagerPagecount(Pager *pPager, Pgno *pnPage){ |
static int pagerOpenWalIfPresent(Pager *pPager){ |
int rc = SQLITE_OK; |
assert( pPager->eState==PAGER_OPEN ); |
- assert( pPager->eLock>=SHARED_LOCK || pPager->noReadlock ); |
+ assert( pPager->eLock>=SHARED_LOCK ); |
if( !pPager->tempFile ){ |
int isWal; /* True if WAL file exists */ |
@@ -3124,6 +3204,7 @@ static int pagerOpenWalIfPresent(Pager *pPager){ |
if( rc ) return rc; |
if( nPage==0 ){ |
rc = sqlite3OsDelete(pPager->pVfs, pPager->zWal, 0); |
+ if( rc==SQLITE_IOERR_DELETE_NOENT ) rc = SQLITE_OK; |
isWal = 0; |
}else{ |
rc = sqlite3OsAccess( |
@@ -3267,13 +3348,13 @@ static int pagerPlaybackSavepoint(Pager *pPager, PagerSavepoint *pSavepoint){ |
*/ |
if( pSavepoint ){ |
u32 ii; /* Loop counter */ |
- i64 offset = pSavepoint->iSubRec*(4+pPager->pageSize); |
+ i64 offset = (i64)pSavepoint->iSubRec*(4+pPager->pageSize); |
if( pagerUseWal(pPager) ){ |
rc = sqlite3WalSavepointUndo(pPager->pWal, pSavepoint->aWalData); |
} |
for(ii=pSavepoint->iSubRec; rc==SQLITE_OK && ii<pPager->nSubRec; ii++){ |
- assert( offset==ii*(4+pPager->pageSize) ); |
+ assert( offset==(i64)ii*(4+pPager->pageSize) ); |
rc = pager_playback_one_page(pPager, &offset, pDone, 0, 1); |
} |
assert( rc!=SQLITE_DONE ); |
@@ -3295,9 +3376,42 @@ void sqlite3PagerSetCachesize(Pager *pPager, int mxPage){ |
} |
/* |
-** Adjust the robustness of the database to damage due to OS crashes |
-** or power failures by changing the number of syncs()s when writing |
-** the rollback journal. There are three levels: |
+** Invoke SQLITE_FCNTL_MMAP_SIZE based on the current value of szMmap. |
+*/ |
+static void pagerFixMaplimit(Pager *pPager){ |
+#if SQLITE_MAX_MMAP_SIZE>0 |
+ sqlite3_file *fd = pPager->fd; |
+ if( isOpen(fd) && fd->pMethods->iVersion>=3 ){ |
+ sqlite3_int64 sz; |
+ sz = pPager->szMmap; |
+ pPager->bUseFetch = (sz>0); |
+ sqlite3OsFileControlHint(pPager->fd, SQLITE_FCNTL_MMAP_SIZE, &sz); |
+ } |
+#endif |
+} |
+ |
+/* |
+** Change the maximum size of any memory mapping made of the database file. |
+*/ |
+void sqlite3PagerSetMmapLimit(Pager *pPager, sqlite3_int64 szMmap){ |
+ pPager->szMmap = szMmap; |
+ pagerFixMaplimit(pPager); |
+} |
+ |
+/* |
+** Free as much memory as possible from the pager. |
+*/ |
+void sqlite3PagerShrink(Pager *pPager){ |
+ sqlite3PcacheShrink(pPager->pPCache); |
+} |
+ |
+/* |
+** Adjust settings of the pager to those specified in the pgFlags parameter. |
+** |
+** 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: |
** |
** OFF sqlite3OsSync() is never called. This is the default |
** for temporary and transient files. |
@@ -3338,28 +3452,36 @@ void sqlite3PagerSetCachesize(Pager *pPager, int mxPage){ |
** and FULL=3. |
*/ |
#ifndef SQLITE_OMIT_PAGER_PRAGMAS |
-void sqlite3PagerSetSafetyLevel( |
+void sqlite3PagerSetFlags( |
Pager *pPager, /* The pager to set safety level for */ |
- int level, /* PRAGMA synchronous. 1=OFF, 2=NORMAL, 3=FULL */ |
- int bFullFsync, /* PRAGMA fullfsync */ |
- int bCkptFullFsync /* PRAGMA checkpoint_fullfsync */ |
+ 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->noSync ){ |
pPager->syncFlags = 0; |
pPager->ckptSyncFlags = 0; |
- }else if( bFullFsync ){ |
+ }else if( pgFlags & PAGER_FULLFSYNC ){ |
pPager->syncFlags = SQLITE_SYNC_FULL; |
pPager->ckptSyncFlags = SQLITE_SYNC_FULL; |
- }else if( bCkptFullFsync ){ |
+ }else if( pgFlags & PAGER_CKPT_FULLFSYNC ){ |
pPager->syncFlags = SQLITE_SYNC_NORMAL; |
pPager->ckptSyncFlags = SQLITE_SYNC_FULL; |
}else{ |
pPager->syncFlags = SQLITE_SYNC_NORMAL; |
pPager->ckptSyncFlags = SQLITE_SYNC_NORMAL; |
} |
+ pPager->walSyncFlags = pPager->syncFlags; |
+ if( pPager->fullSync ){ |
+ pPager->walSyncFlags |= WAL_SYNC_TRANSACTIONS; |
+ } |
+ if( pgFlags & PAGER_CACHESPILL ){ |
+ pPager->doNotSpill &= ~SPILLFLAG_OFF; |
+ }else{ |
+ pPager->doNotSpill |= SPILLFLAG_OFF; |
+ } |
} |
#endif |
@@ -3430,9 +3552,16 @@ void sqlite3PagerSetBusyhandler( |
Pager *pPager, /* Pager object */ |
int (*xBusyHandler)(void *), /* Pointer to busy-handler function */ |
void *pBusyHandlerArg /* Argument to pass to xBusyHandler */ |
-){ |
+){ |
pPager->xBusyHandler = xBusyHandler; |
pPager->pBusyHandlerArg = pBusyHandlerArg; |
+ |
+ if( isOpen(pPager->fd) ){ |
+ void **ap = (void **)&pPager->xBusyHandler; |
+ assert( ((int(*)(void *))(ap[0]))==xBusyHandler ); |
+ assert( ap[1]==pBusyHandlerArg ); |
+ sqlite3OsFileControlHint(pPager->fd, SQLITE_FCNTL_BUSYHANDLER, (void *)ap); |
+ } |
} |
/* |
@@ -3497,11 +3626,15 @@ int sqlite3PagerSetPagesize(Pager *pPager, u32 *pPageSize, int nReserve){ |
if( rc==SQLITE_OK ){ |
pager_reset(pPager); |
- pPager->dbSize = (Pgno)(nByte/pageSize); |
- pPager->pageSize = pageSize; |
+ rc = sqlite3PcacheSetPageSize(pPager->pPCache, pageSize); |
+ } |
+ if( rc==SQLITE_OK ){ |
sqlite3PageFree(pPager->pTmpSpace); |
pPager->pTmpSpace = pNew; |
- sqlite3PcacheSetPageSize(pPager->pPCache, pageSize); |
+ pPager->dbSize = (Pgno)((nByte+pageSize-1)/pageSize); |
+ pPager->pageSize = pageSize; |
+ }else{ |
+ sqlite3PageFree(pNew); |
} |
} |
@@ -3511,6 +3644,7 @@ int sqlite3PagerSetPagesize(Pager *pPager, u32 *pPageSize, int nReserve){ |
assert( nReserve>=0 && nReserve<1000 ); |
pPager->nReserve = (i16)nReserve; |
pagerReportSize(pPager); |
+ pagerFixMaplimit(pPager); |
} |
return rc; |
} |
@@ -3634,7 +3768,7 @@ static int pager_wait_on_lock(Pager *pPager, int locktype){ |
int rc; /* Return code */ |
/* Check that this is either a no-op (because the requested lock is |
- ** already held, or one of the transistions that the busy-handler |
+ ** already held), or one of the transitions that the busy-handler |
** may be invoked during, according to the comment above |
** sqlite3PagerSetBusyhandler(). |
*/ |
@@ -3664,7 +3798,7 @@ static int pager_wait_on_lock(Pager *pPager, int locktype){ |
** dirty page were to be discarded from the cache via the pagerStress() |
** routine, pagerStress() would not write the current page content to |
** the database file. If a savepoint transaction were rolled back after |
-** this happened, the correct behaviour would be to restore the current |
+** this happened, the correct behavior would be to restore the current |
** content of the page. However, since this content is not present in either |
** the database file or the portion of the rollback journal and |
** sub-journal rolled back the content could not be restored and the |
@@ -3688,12 +3822,26 @@ static void assertTruncateConstraint(Pager *pPager){ |
** function does not actually modify the database file on disk. It |
** just sets the internal state of the pager object so that the |
** truncation will be done when the current transaction is committed. |
+** |
+** This function is only called right before committing a transaction. |
+** Once this function has been called, the transaction must either be |
+** rolled back or committed. It is not safe to call this function and |
+** then continue writing to the database. |
*/ |
void sqlite3PagerTruncateImage(Pager *pPager, Pgno nPage){ |
assert( pPager->dbSize>=nPage ); |
assert( pPager->eState>=PAGER_WRITER_CACHEMOD ); |
pPager->dbSize = nPage; |
- assertTruncateConstraint(pPager); |
+ |
+ /* At one point the code here called assertTruncateConstraint() to |
+ ** ensure that all pages being truncated away by this operation are, |
+ ** if one or more savepoints are open, present in the savepoint |
+ ** journal so that they can be restored if the savepoint is rolled |
+ ** back. This is no longer necessary as this function is now only |
+ ** called right before committing a transaction. So although the |
+ ** Pager object may still have open savepoints (Pager.nSavepoint!=0), |
+ ** they cannot be rolled back. So the assertTruncateConstraint() call |
+ ** is no longer correct. */ |
} |
@@ -3723,6 +3871,81 @@ static int pagerSyncHotJournal(Pager *pPager){ |
} |
/* |
+** Obtain a reference to a memory mapped page object for page number pgno. |
+** The new object will use the pointer pData, obtained from xFetch(). |
+** If successful, set *ppPage to point to the new page reference |
+** and return SQLITE_OK. Otherwise, return an SQLite error code and set |
+** *ppPage to zero. |
+** |
+** Page references obtained by calling this function should be released |
+** by calling pagerReleaseMapPage(). |
+*/ |
+static int pagerAcquireMapPage( |
+ Pager *pPager, /* Pager object */ |
+ Pgno pgno, /* Page number */ |
+ void *pData, /* xFetch()'d data for this page */ |
+ PgHdr **ppPage /* OUT: Acquired page object */ |
+){ |
+ PgHdr *p; /* Memory mapped page to return */ |
+ |
+ if( pPager->pMmapFreelist ){ |
+ *ppPage = p = pPager->pMmapFreelist; |
+ pPager->pMmapFreelist = p->pDirty; |
+ p->pDirty = 0; |
+ memset(p->pExtra, 0, pPager->nExtra); |
+ }else{ |
+ *ppPage = p = (PgHdr *)sqlite3MallocZero(sizeof(PgHdr) + pPager->nExtra); |
+ if( p==0 ){ |
+ sqlite3OsUnfetch(pPager->fd, (i64)(pgno-1) * pPager->pageSize, pData); |
+ return SQLITE_NOMEM; |
+ } |
+ p->pExtra = (void *)&p[1]; |
+ p->flags = PGHDR_MMAP; |
+ p->nRef = 1; |
+ p->pPager = pPager; |
+ } |
+ |
+ assert( p->pExtra==(void *)&p[1] ); |
+ assert( p->pPage==0 ); |
+ assert( p->flags==PGHDR_MMAP ); |
+ assert( p->pPager==pPager ); |
+ assert( p->nRef==1 ); |
+ |
+ p->pgno = pgno; |
+ p->pData = pData; |
+ pPager->nMmapOut++; |
+ |
+ return SQLITE_OK; |
+} |
+ |
+/* |
+** Release a reference to page pPg. pPg must have been returned by an |
+** earlier call to pagerAcquireMapPage(). |
+*/ |
+static void pagerReleaseMapPage(PgHdr *pPg){ |
+ Pager *pPager = pPg->pPager; |
+ pPager->nMmapOut--; |
+ pPg->pDirty = pPager->pMmapFreelist; |
+ pPager->pMmapFreelist = pPg; |
+ |
+ assert( pPager->fd->pMethods->iVersion>=3 ); |
+ sqlite3OsUnfetch(pPager->fd, (i64)(pPg->pgno-1)*pPager->pageSize, pPg->pData); |
+} |
+ |
+/* |
+** Free all PgHdr objects stored in the Pager.pMmapFreelist list. |
+*/ |
+static void pagerFreeMapHdrs(Pager *pPager){ |
+ PgHdr *p; |
+ PgHdr *pNext; |
+ for(p=pPager->pMmapFreelist; p; p=pNext){ |
+ pNext = p->pDirty; |
+ sqlite3_free(p); |
+ } |
+} |
+ |
+ |
+/* |
** Shutdown the page cache. Free all memory and close all files. |
** |
** If a transaction was in progress when this routine is called, that |
@@ -3739,8 +3962,10 @@ static int pagerSyncHotJournal(Pager *pPager){ |
int sqlite3PagerClose(Pager *pPager){ |
u8 *pTmp = (u8 *)pPager->pTmpSpace; |
+ assert( assert_pager_state(pPager) ); |
disable_simulated_io_errors(); |
sqlite3BeginBenignMalloc(); |
+ pagerFreeMapHdrs(pPager); |
/* pPager->errCode = 0; */ |
pPager->exclusiveMode = 0; |
#ifndef SQLITE_OMIT_WAL |
@@ -3810,7 +4035,7 @@ void sqlite3PagerRef(DbPage *pPg){ |
** |
** If the Pager.noSync flag is set, then this function is a no-op. |
** Otherwise, the actions required depend on the journal-mode and the |
-** device characteristics of the the file-system, as follows: |
+** device characteristics of the file-system, as follows: |
** |
** * If the journal file is an in-memory journal file, no action need |
** be taken. |
@@ -4002,9 +4227,12 @@ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){ |
** file size will be. |
*/ |
assert( rc!=SQLITE_OK || isOpen(pPager->fd) ); |
- if( rc==SQLITE_OK && pPager->dbSize>pPager->dbHintSize ){ |
+ if( rc==SQLITE_OK |
+ && pPager->dbHintSize<pPager->dbSize |
+ && (pList->pDirty || pList->pgno>pPager->dbHintSize) |
+ ){ |
sqlite3_int64 szFile = pPager->pageSize * (sqlite3_int64)pPager->dbSize; |
- sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_SIZE_HINT, &szFile); |
+ sqlite3OsFileControlHint(pPager->fd, SQLITE_FCNTL_SIZE_HINT, &szFile); |
pPager->dbHintSize = pPager->dbSize; |
} |
@@ -4042,6 +4270,7 @@ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){ |
if( pgno>pPager->dbFileSize ){ |
pPager->dbFileSize = pgno; |
} |
+ pPager->aStat[PAGER_STAT_WRITE]++; |
/* Update any backup objects copying the contents of this pager. */ |
sqlite3BackupUpdate(pPager->pBackup, pgno, (u8*)pList->pData); |
@@ -4050,7 +4279,6 @@ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){ |
PAGERID(pPager), pgno, pager_pagehash(pList))); |
IOTRACE(("PGOUT %p %d\n", pPager, pgno)); |
PAGER_INCR(sqlite3_pager_writedb_count); |
- PAGER_INCR(pPager->nWrite); |
}else{ |
PAGERTRACE(("NOSTORE %d page %d\n", PAGERID(pPager), pgno)); |
} |
@@ -4104,7 +4332,7 @@ static int subjournalPage(PgHdr *pPg){ |
assert( isOpen(pPager->jfd) || pagerUseWal(pPager) ); |
assert( isOpen(pPager->sjfd) || pPager->nSubRec==0 ); |
assert( pagerUseWal(pPager) |
- || pageInJournal(pPg) |
+ || pageInJournal(pPager, pPg) |
|| pPg->pgno>pPager->dbOrigSize |
); |
rc = openSubJournal(pPager); |
@@ -4113,7 +4341,7 @@ static int subjournalPage(PgHdr *pPg){ |
** write the journal record into the file. */ |
if( rc==SQLITE_OK ){ |
void *pData = pPg->pData; |
- i64 offset = pPager->nSubRec*(4+pPager->pageSize); |
+ i64 offset = (i64)pPager->nSubRec*(4+pPager->pageSize); |
char *pData2; |
CODEC2(pPager, pData, pPg->pgno, 7, return SQLITE_NOMEM, pData2); |
@@ -4158,24 +4386,30 @@ static int pagerStress(void *p, PgHdr *pPg){ |
assert( pPg->pPager==pPager ); |
assert( pPg->flags&PGHDR_DIRTY ); |
- /* The doNotSyncSpill flag is set during times when doing a sync of |
+ /* The doNotSpill NOSYNC bit is set during times when doing a sync of |
** journal (and adding a new header) is not allowed. This occurs |
** during calls to sqlite3PagerWrite() while trying to journal multiple |
** pages belonging to the same sector. |
** |
- ** The doNotSpill flag inhibits all cache spilling regardless of whether |
- ** or not a sync is required. This is set during a rollback. |
+ ** The doNotSpill ROLLBACK and OFF bits inhibits all cache spilling |
+ ** regardless of whether or not a sync is required. This is set during |
+ ** a rollback or by user request, respectively. |
** |
** Spilling is also prohibited when in an error state since that could |
- ** lead to database corruption. In the current implementaton it |
- ** is impossible for sqlite3PCacheFetch() to be called with createFlag==1 |
+ ** lead to database corruption. In the current implementation it |
+ ** is impossible for sqlite3PcacheFetch() to be called with createFlag==3 |
** while in the error state, hence it is impossible for this routine to |
** be called in the error state. Nevertheless, we include a NEVER() |
** test for the error state as a safeguard against future changes. |
*/ |
if( NEVER(pPager->errCode) ) return SQLITE_OK; |
- if( pPager->doNotSpill ) return SQLITE_OK; |
- if( pPager->doNotSyncSpill && (pPg->flags & PGHDR_NEED_SYNC)!=0 ){ |
+ testcase( pPager->doNotSpill & SPILLFLAG_ROLLBACK ); |
+ testcase( pPager->doNotSpill & SPILLFLAG_OFF ); |
+ testcase( pPager->doNotSpill & SPILLFLAG_NOSYNC ); |
+ if( pPager->doNotSpill |
+ && ((pPager->doNotSpill & (SPILLFLAG_ROLLBACK|SPILLFLAG_OFF))!=0 |
+ || (pPg->flags & PGHDR_NEED_SYNC)!=0) |
+ ){ |
return SQLITE_OK; |
} |
@@ -4186,7 +4420,7 @@ static int pagerStress(void *p, PgHdr *pPg){ |
rc = subjournalPage(pPg); |
} |
if( rc==SQLITE_OK ){ |
- rc = pagerWalFrames(pPager, pPg, 0, 0, 0); |
+ rc = pagerWalFrames(pPager, pPg, 0, 0); |
} |
}else{ |
@@ -4265,7 +4499,7 @@ static int pagerStress(void *p, PgHdr *pPg){ |
** |
** The flags argument is used to specify properties that affect the |
** operation of the pager. It should be passed some bitwise combination |
-** of the PAGER_OMIT_JOURNAL and PAGER_NO_READLOCK flags. |
+** of the PAGER_* flags. |
** |
** The vfsFlags parameter is a bitmask to pass to the flags parameter |
** of the xOpen() method of the supplied VFS when opening files. |
@@ -4296,9 +4530,10 @@ int sqlite3PagerOpen( |
char *zPathname = 0; /* Full path to database file */ |
int nPathname = 0; /* Number of bytes in zPathname */ |
int useJournal = (flags & PAGER_OMIT_JOURNAL)==0; /* False to omit journal */ |
- int noReadlock = (flags & PAGER_NO_READLOCK)!=0; /* True to omit read-lock */ |
int pcacheSize = sqlite3PcacheSize(); /* Bytes to allocate for PCache */ |
u32 szPageDflt = SQLITE_DEFAULT_PAGE_SIZE; /* Default page size */ |
+ const char *zUri = 0; /* URI args to copy */ |
+ 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 |
@@ -4320,7 +4555,12 @@ int sqlite3PagerOpen( |
#ifndef SQLITE_OMIT_MEMORYDB |
if( flags & PAGER_MEMORY ){ |
memDb = 1; |
- zFilename = 0; |
+ if( zFilename && zFilename[0] ){ |
+ zPathname = sqlite3DbStrDup(0, zFilename); |
+ if( zPathname==0 ) return SQLITE_NOMEM; |
+ nPathname = sqlite3Strlen30(zPathname); |
+ zFilename = 0; |
+ } |
} |
#endif |
@@ -4329,14 +4569,22 @@ int sqlite3PagerOpen( |
** leave both nPathname and zPathname set to 0. |
*/ |
if( zFilename && zFilename[0] ){ |
+ const char *z; |
nPathname = pVfs->mxPathname+1; |
- zPathname = sqlite3Malloc(nPathname*2); |
+ zPathname = sqlite3DbMallocRaw(0, nPathname*2); |
if( zPathname==0 ){ |
return SQLITE_NOMEM; |
} |
zPathname[0] = 0; /* Make sure initialized even if FullPathname() fails */ |
rc = sqlite3OsFullPathname(pVfs, zFilename, nPathname, zPathname); |
nPathname = sqlite3Strlen30(zPathname); |
+ z = zUri = &zFilename[sqlite3Strlen30(zFilename)+1]; |
+ while( *z ){ |
+ z += sqlite3Strlen30(z)+1; |
+ z += sqlite3Strlen30(z)+1; |
+ } |
+ nUri = (int)(&z[1] - zUri); |
+ assert( nUri>=0 ); |
if( rc==SQLITE_OK && nPathname+8>pVfs->mxPathname ){ |
/* This branch is taken when the journal path required by |
** the database being opened will be more than pVfs->mxPathname |
@@ -4347,7 +4595,7 @@ int sqlite3PagerOpen( |
rc = SQLITE_CANTOPEN_BKPT; |
} |
if( rc!=SQLITE_OK ){ |
- sqlite3_free(zPathname); |
+ sqlite3DbFree(0, zPathname); |
return rc; |
} |
} |
@@ -4369,15 +4617,15 @@ int sqlite3PagerOpen( |
ROUND8(pcacheSize) + /* PCache object */ |
ROUND8(pVfs->szOsFile) + /* The main db file */ |
journalFileSize * 2 + /* The two journal files */ |
- nPathname + 1 + /* zFilename */ |
- nPathname + 8 + 1 /* zJournal */ |
+ nPathname + 1 + nUri + /* zFilename */ |
+ nPathname + 8 + 2 /* zJournal */ |
#ifndef SQLITE_OMIT_WAL |
- + nPathname + 4 + 1 /* zWal */ |
+ + nPathname + 4 + 2 /* zWal */ |
#endif |
); |
assert( EIGHT_BYTE_ALIGNMENT(SQLITE_INT_TO_PTR(journalFileSize)) ); |
if( !pPtr ){ |
- sqlite3_free(zPathname); |
+ sqlite3DbFree(0, zPathname); |
return SQLITE_NOMEM; |
} |
pPager = (Pager*)(pPtr); |
@@ -4391,16 +4639,19 @@ int sqlite3PagerOpen( |
/* Fill in the Pager.zFilename and Pager.zJournal buffers, if required. */ |
if( zPathname ){ |
assert( nPathname>0 ); |
- pPager->zJournal = (char*)(pPtr += nPathname + 1); |
+ pPager->zJournal = (char*)(pPtr += nPathname + 1 + nUri); |
memcpy(pPager->zFilename, zPathname, nPathname); |
+ if( nUri ) memcpy(&pPager->zFilename[nPathname+1], zUri, nUri); |
memcpy(pPager->zJournal, zPathname, nPathname); |
- memcpy(&pPager->zJournal[nPathname], "-journal", 8); |
+ memcpy(&pPager->zJournal[nPathname], "-journal\000", 8+2); |
+ sqlite3FileSuffix3(pPager->zFilename, pPager->zJournal); |
#ifndef SQLITE_OMIT_WAL |
pPager->zWal = &pPager->zJournal[nPathname+8+1]; |
memcpy(pPager->zWal, zPathname, nPathname); |
- memcpy(&pPager->zWal[nPathname], "-wal", 4); |
+ memcpy(&pPager->zWal[nPathname], "-wal\000", 4+1); |
+ sqlite3FileSuffix3(pPager->zFilename, pPager->zWal); |
#endif |
- sqlite3_free(zPathname); |
+ sqlite3DbFree(0, zPathname); |
} |
pPager->pVfs = pVfs; |
pPager->vfsFlags = vfsFlags; |
@@ -4421,30 +4672,38 @@ int sqlite3PagerOpen( |
** + The value returned by sqlite3OsSectorSize() |
** + The largest page size that can be written atomically. |
*/ |
- if( rc==SQLITE_OK && !readOnly ){ |
- setSectorSize(pPager); |
- assert(SQLITE_DEFAULT_PAGE_SIZE<=SQLITE_MAX_DEFAULT_PAGE_SIZE); |
- if( szPageDflt<pPager->sectorSize ){ |
- if( pPager->sectorSize>SQLITE_MAX_DEFAULT_PAGE_SIZE ){ |
- szPageDflt = SQLITE_MAX_DEFAULT_PAGE_SIZE; |
- }else{ |
- szPageDflt = (u32)pPager->sectorSize; |
+ if( rc==SQLITE_OK ){ |
+ int iDc = sqlite3OsDeviceCharacteristics(pPager->fd); |
+ if( !readOnly ){ |
+ setSectorSize(pPager); |
+ assert(SQLITE_DEFAULT_PAGE_SIZE<=SQLITE_MAX_DEFAULT_PAGE_SIZE); |
+ if( szPageDflt<pPager->sectorSize ){ |
+ if( pPager->sectorSize>SQLITE_MAX_DEFAULT_PAGE_SIZE ){ |
+ szPageDflt = SQLITE_MAX_DEFAULT_PAGE_SIZE; |
+ }else{ |
+ szPageDflt = (u32)pPager->sectorSize; |
+ } |
} |
- } |
#ifdef SQLITE_ENABLE_ATOMIC_WRITE |
- { |
- int iDc = sqlite3OsDeviceCharacteristics(pPager->fd); |
- int ii; |
- assert(SQLITE_IOCAP_ATOMIC512==(512>>8)); |
- assert(SQLITE_IOCAP_ATOMIC64K==(65536>>8)); |
- assert(SQLITE_MAX_DEFAULT_PAGE_SIZE<=65536); |
- for(ii=szPageDflt; ii<=SQLITE_MAX_DEFAULT_PAGE_SIZE; ii=ii*2){ |
- if( iDc&(SQLITE_IOCAP_ATOMIC|(ii>>8)) ){ |
- szPageDflt = ii; |
+ { |
+ int ii; |
+ assert(SQLITE_IOCAP_ATOMIC512==(512>>8)); |
+ assert(SQLITE_IOCAP_ATOMIC64K==(65536>>8)); |
+ assert(SQLITE_MAX_DEFAULT_PAGE_SIZE<=65536); |
+ for(ii=szPageDflt; ii<=SQLITE_MAX_DEFAULT_PAGE_SIZE; ii=ii*2){ |
+ if( iDc&(SQLITE_IOCAP_ATOMIC|(ii>>8)) ){ |
+ szPageDflt = ii; |
+ } |
} |
} |
- } |
#endif |
+ } |
+ pPager->noLock = sqlite3_uri_boolean(zFilename, "nolock", 0); |
+ if( (iDc & SQLITE_IOCAP_IMMUTABLE)!=0 |
+ || sqlite3_uri_boolean(zFilename, "immutable", 0) ){ |
+ vfsFlags |= SQLITE_OPEN_READONLY; |
+ goto act_like_temp_file; |
+ } |
} |
}else{ |
/* If a temporary file is requested, it is not opened immediately. |
@@ -4454,10 +4713,14 @@ int sqlite3PagerOpen( |
** This branch is also run for an in-memory database. An in-memory |
** database is the same as a temp-file that is never written out to |
** disk and uses an in-memory rollback journal. |
+ ** |
+ ** This branch also runs for files marked as immutable. |
*/ |
+act_like_temp_file: |
tempFile = 1; |
- pPager->eState = PAGER_READER; |
- pPager->eLock = EXCLUSIVE_LOCK; |
+ pPager->eState = PAGER_READER; /* Pretend we already have a lock */ |
+ pPager->eLock = EXCLUSIVE_LOCK; /* Pretend we are in EXCLUSIVE locking mode */ |
+ pPager->noLock = 1; /* Do no locking */ |
readOnly = (vfsFlags&SQLITE_OPEN_READONLY); |
} |
@@ -4470,27 +4733,27 @@ int sqlite3PagerOpen( |
testcase( rc!=SQLITE_OK ); |
} |
- /* If an error occurred in either of the blocks above, free the |
- ** Pager structure and close the file. |
+ /* Initialize the PCache object. */ |
+ if( rc==SQLITE_OK ){ |
+ assert( nExtra<1000 ); |
+ nExtra = ROUND8(nExtra); |
+ rc = sqlite3PcacheOpen(szPageDflt, nExtra, !memDb, |
+ !memDb?pagerStress:0, (void *)pPager, pPager->pPCache); |
+ } |
+ |
+ /* If an error occurred above, free the Pager structure and close the file. |
*/ |
if( rc!=SQLITE_OK ){ |
- assert( !pPager->pTmpSpace ); |
sqlite3OsClose(pPager->fd); |
+ sqlite3PageFree(pPager->pTmpSpace); |
sqlite3_free(pPager); |
return rc; |
} |
- /* Initialize the PCache object. */ |
- assert( nExtra<1000 ); |
- nExtra = ROUND8(nExtra); |
- sqlite3PcacheOpen(szPageDflt, nExtra, !memDb, |
- !memDb?pagerStress:0, (void *)pPager, pPager->pPCache); |
- |
PAGERTRACE(("OPEN %d %s\n", FILEHANDLEID(pPager->fd), pPager->zFilename)); |
IOTRACE(("OPEN %p %s\n", pPager, pPager->zFilename)) |
pPager->useJournal = (u8)useJournal; |
- pPager->noReadlock = (noReadlock && readOnly) ?1:0; |
/* pPager->stmtOpen = 0; */ |
/* pPager->stmtInUse = 0; */ |
/* pPager->nRef = 0; */ |
@@ -4499,9 +4762,6 @@ int sqlite3PagerOpen( |
/* pPager->nPage = 0; */ |
pPager->mxPgno = SQLITE_MAX_PAGE_COUNT; |
/* pPager->state = PAGER_UNLOCK; */ |
-#if 0 |
- assert( pPager->state == (tempFile ? PAGER_EXCLUSIVE : PAGER_UNLOCK) ); |
-#endif |
/* pPager->errMask = 0; */ |
pPager->tempFile = (u8)tempFile; |
assert( tempFile==PAGER_LOCKINGMODE_NORMAL |
@@ -4513,9 +4773,17 @@ int sqlite3PagerOpen( |
pPager->readOnly = (u8)readOnly; |
assert( useJournal || pPager->tempFile ); |
pPager->noSync = pPager->tempFile; |
- pPager->fullSync = pPager->noSync ?0:1; |
- pPager->syncFlags = pPager->noSync ? 0 : SQLITE_SYNC_NORMAL; |
- pPager->ckptSyncFlags = pPager->syncFlags; |
+ if( pPager->noSync ){ |
+ assert( pPager->fullSync==0 ); |
+ assert( pPager->syncFlags==0 ); |
+ assert( pPager->walSyncFlags==0 ); |
+ assert( pPager->ckptSyncFlags==0 ); |
+ }else{ |
+ pPager->fullSync = 1; |
+ pPager->syncFlags = SQLITE_SYNC_NORMAL; |
+ pPager->walSyncFlags = SQLITE_SYNC_NORMAL | WAL_SYNC_TRANSACTIONS; |
+ pPager->ckptSyncFlags = SQLITE_SYNC_NORMAL; |
+ } |
/* pPager->pFirst = 0; */ |
/* pPager->pFirstSynced = 0; */ |
/* pPager->pLast = 0; */ |
@@ -4532,12 +4800,37 @@ int sqlite3PagerOpen( |
/* pPager->pBusyHandlerArg = 0; */ |
pPager->xReiniter = xReinit; |
/* memset(pPager->aHash, 0, sizeof(pPager->aHash)); */ |
+ /* pPager->szMmap = SQLITE_DEFAULT_MMAP_SIZE // will be set by btree.c */ |
*ppPager = pPager; |
return SQLITE_OK; |
} |
+/* Verify that the database file has not be deleted or renamed out from |
+** under the pager. Return SQLITE_OK if the database is still were it ought |
+** to be on disk. Return non-zero (SQLITE_READONLY_DBMOVED or some other error |
+** code from sqlite3OsAccess()) if the database has gone missing. |
+*/ |
+static int databaseIsUnmoved(Pager *pPager){ |
+ int bHasMoved = 0; |
+ int rc; |
+ |
+ if( pPager->tempFile ) return SQLITE_OK; |
+ if( pPager->dbSize==0 ) return SQLITE_OK; |
+ assert( pPager->zFilename && pPager->zFilename[0] ); |
+ rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_HAS_MOVED, &bHasMoved); |
+ if( rc==SQLITE_NOTFOUND ){ |
+ /* If the HAS_MOVED file-control is unimplemented, assume that the file |
+ ** has not been moved. That is the historical behavior of SQLite: prior to |
+ ** version 3.8.3, it never checked */ |
+ rc = SQLITE_OK; |
+ }else if( rc==SQLITE_OK && bHasMoved ){ |
+ rc = SQLITE_READONLY_DBMOVED; |
+ } |
+ return rc; |
+} |
+ |
/* |
** This function is called after transitioning from PAGER_UNLOCK to |
@@ -4603,15 +4896,17 @@ static int hasHotJournal(Pager *pPager, int *pExists){ |
if( rc==SQLITE_OK && !locked ){ |
Pgno nPage; /* Number of pages in database file */ |
- /* Check the size of the database file. If it consists of 0 pages, |
- ** then delete the journal file. See the header comment above for |
- ** the reasoning here. Delete the obsolete journal file under |
- ** a RESERVED lock to avoid race conditions and to avoid violating |
- ** [H33020]. |
- */ |
rc = pagerPagecount(pPager, &nPage); |
if( rc==SQLITE_OK ){ |
- if( nPage==0 ){ |
+ /* If the database is zero pages in size, that means that either (1) the |
+ ** journal is a remnant from a prior database with the same name where |
+ ** the database file but not the journal was deleted, or (2) the initial |
+ ** transaction that populates a new database is being rolled back. |
+ ** In either case, the journal file can be deleted. However, take care |
+ ** not to delete the journal file if it is already open due to |
+ ** journal_mode=PERSIST. |
+ */ |
+ if( nPage==0 && !jrnlOpen ){ |
sqlite3BeginBenignMalloc(); |
if( pagerLockDb(pPager, RESERVED_LOCK)==SQLITE_OK ){ |
sqlite3OsDelete(pVfs, pPager->zJournal, 0); |
@@ -4641,7 +4936,7 @@ static int hasHotJournal(Pager *pPager, int *pExists){ |
*pExists = (first!=0); |
}else if( rc==SQLITE_CANTOPEN ){ |
/* If we cannot open the rollback journal file in order to see if |
- ** its has a zero header, that might be due to an I/O error, or |
+ ** it has a zero header, that might be due to an I/O error, or |
** it might be due to the race condition described above and in |
** ticket #3883. Either way, assume that the journal is hot. |
** This might be a false positive. But if it is, then the |
@@ -4704,14 +4999,11 @@ int sqlite3PagerSharedLock(Pager *pPager){ |
int bHotJournal = 1; /* True if there exists a hot journal-file */ |
assert( !MEMDB ); |
- assert( pPager->noReadlock==0 || pPager->readOnly ); |
- if( pPager->noReadlock==0 ){ |
- rc = pager_wait_on_lock(pPager, SHARED_LOCK); |
- if( rc!=SQLITE_OK ){ |
- assert( pPager->eLock==NO_LOCK || pPager->eLock==UNKNOWN_LOCK ); |
- goto failed; |
- } |
+ rc = pager_wait_on_lock(pPager, SHARED_LOCK); |
+ if( rc!=SQLITE_OK ){ |
+ assert( pPager->eLock==NO_LOCK || pPager->eLock==UNKNOWN_LOCK ); |
+ goto failed; |
} |
/* If a journal file exists, and there is no RESERVED lock on the |
@@ -4724,6 +5016,11 @@ int sqlite3PagerSharedLock(Pager *pPager){ |
goto failed; |
} |
if( bHotJournal ){ |
+ if( pPager->readOnly ){ |
+ rc = SQLITE_READONLY_ROLLBACK; |
+ goto failed; |
+ } |
+ |
/* Get an EXCLUSIVE lock on the database file. At this point it is |
** important that a RESERVED lock is not obtained on the way to the |
** EXCLUSIVE lock. If it were, another process might open the |
@@ -4821,9 +5118,11 @@ int sqlite3PagerSharedLock(Pager *pPager){ |
); |
} |
- if( !pPager->tempFile |
- && (pPager->pBackup || sqlite3PcachePagecount(pPager->pPCache)>0) |
- ){ |
+ if( !pPager->tempFile && ( |
+ pPager->pBackup |
+ || sqlite3PcachePagecount(pPager->pPCache)>0 |
+ || USEFETCH(pPager) |
+ )){ |
/* The shared-lock has just been acquired on the database file |
** and there are already pages in the cache (from a previous |
** read or write transaction). Check to see if the database |
@@ -4849,7 +5148,7 @@ int sqlite3PagerSharedLock(Pager *pPager){ |
if( nPage>0 ){ |
IOTRACE(("CKVERS %p %d\n", pPager, sizeof(dbFileVers))); |
rc = sqlite3OsRead(pPager->fd, &dbFileVers, sizeof(dbFileVers), 24); |
- if( rc!=SQLITE_OK ){ |
+ if( rc!=SQLITE_OK && rc!=SQLITE_IOERR_SHORT_READ ){ |
goto failed; |
} |
}else{ |
@@ -4858,6 +5157,16 @@ int sqlite3PagerSharedLock(Pager *pPager){ |
if( memcmp(pPager->dbFileVers, dbFileVers, sizeof(dbFileVers))!=0 ){ |
pager_reset(pPager); |
+ |
+ /* Unmap the database file. It is possible that external processes |
+ ** may have truncated the database file and then extended it back |
+ ** to its original size while this process was not holding a lock. |
+ ** In this case there may exist a Pager.pMap mapping that appears |
+ ** to be the right size but is not actually valid. Avoid this |
+ ** possibility by unmapping the db here. */ |
+ if( USEFETCH(pPager) ){ |
+ sqlite3OsUnfetch(pPager->fd, 0, 0); |
+ } |
} |
} |
@@ -4899,7 +5208,7 @@ int sqlite3PagerSharedLock(Pager *pPager){ |
** nothing to rollback, so this routine is a no-op. |
*/ |
static void pagerUnlockIfUnused(Pager *pPager){ |
- if( (sqlite3PcacheRefCount(pPager->pPCache)==0) ){ |
+ if( pPager->nMmapOut==0 && (sqlite3PcacheRefCount(pPager->pPCache)==0) ){ |
pagerUnlockAndRollback(pPager); |
} |
} |
@@ -4927,7 +5236,7 @@ static void pagerUnlockIfUnused(Pager *pPager){ |
** 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 seperate scenarios: |
+** of the page. This occurs in two scenarios: |
** |
** a) When reading a free-list leaf page from the database, and |
** |
@@ -4958,13 +5267,27 @@ int sqlite3PagerAcquire( |
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 noContent /* Do not bother reading content from disk if true */ |
+ int flags /* PAGER_GET_XXX flags */ |
){ |
- int rc; |
- PgHdr *pPg; |
+ 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 |
+ ); |
assert( pPager->eState>=PAGER_READER ); |
assert( assert_pager_state(pPager) ); |
+ assert( noContent==0 || bMmapOk==0 ); |
if( pgno==0 ){ |
return SQLITE_CORRUPT_BKPT; |
@@ -4975,7 +5298,48 @@ int sqlite3PagerAcquire( |
if( pPager->errCode!=SQLITE_OK ){ |
rc = pPager->errCode; |
}else{ |
- rc = sqlite3PcacheFetch(pPager->pPCache, pgno, 1, ppPage); |
+ 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; |
+ } |
+ pPg = *ppPage = sqlite3PcacheFetchFinish(pPager->pPCache, pgno, pBase); |
+ if( pPg==0 ) rc = SQLITE_NOMEM; |
+ } |
} |
if( rc!=SQLITE_OK ){ |
@@ -4992,14 +5356,13 @@ int sqlite3PagerAcquire( |
/* In this case the pcache already contains an initialized copy of |
** the page. Return without further ado. */ |
assert( pgno<=PAGER_MAX_PGNO && pgno!=PAGER_MJ_PGNO(pPager) ); |
- PAGER_INCR(pPager->nHit); |
+ pPager->aStat[PAGER_STAT_HIT]++; |
return SQLITE_OK; |
}else{ |
/* The pager cache has created a new page. Its content needs to |
** be initialized. */ |
- PAGER_INCR(pPager->nMiss); |
pPg = *ppPage; |
pPg->pPager = pPager; |
@@ -5034,8 +5397,13 @@ int sqlite3PagerAcquire( |
memset(pPg->pData, 0, pPager->pageSize); |
IOTRACE(("ZERO %p %d\n", pPager, pgno)); |
}else{ |
+ if( pagerUseWal(pPager) && bMmapOk==0 ){ |
+ rc = sqlite3WalFindFrame(pPager->pWal, pgno, &iFrame); |
+ if( rc!=SQLITE_OK ) goto pager_acquire_err; |
+ } |
assert( pPg->pPager==pPager ); |
- rc = readDbPage(pPg); |
+ pPager->aStat[PAGER_STAT_MISS]++; |
+ rc = readDbPage(pPg, iFrame); |
if( rc!=SQLITE_OK ){ |
goto pager_acquire_err; |
} |
@@ -5068,13 +5436,12 @@ pager_acquire_err: |
** has ever happened. |
*/ |
DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno){ |
- PgHdr *pPg = 0; |
+ sqlite3_pcache_page *pPage; |
assert( pPager!=0 ); |
assert( pgno!=0 ); |
assert( pPager->pPCache!=0 ); |
- assert( pPager->eState>=PAGER_READER && pPager->eState!=PAGER_ERROR ); |
- sqlite3PcacheFetch(pPager->pPCache, pgno, 0, &pPg); |
- return pPg; |
+ pPage = sqlite3PcacheFetch(pPager->pPCache, pgno, 0); |
+ return sqlite3PcacheFetchFinish(pPager->pPCache, pgno, pPage); |
} |
/* |
@@ -5085,12 +5452,19 @@ DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno){ |
** are released, a rollback occurs and the lock on the database is |
** removed. |
*/ |
-void sqlite3PagerUnref(DbPage *pPg){ |
- if( pPg ){ |
- Pager *pPager = pPg->pPager; |
+void sqlite3PagerUnrefNotNull(DbPage *pPg){ |
+ Pager *pPager; |
+ assert( pPg!=0 ); |
+ pPager = pPg->pPager; |
+ if( pPg->flags & PGHDR_MMAP ){ |
+ pagerReleaseMapPage(pPg); |
+ }else{ |
sqlite3PcacheRelease(pPg); |
- pagerUnlockIfUnused(pPager); |
} |
+ pagerUnlockIfUnused(pPager); |
+} |
+void sqlite3PagerUnref(DbPage *pPg){ |
+ if( pPg ) sqlite3PagerUnrefNotNull(pPg); |
} |
#if defined(__APPLE__) |
@@ -5159,31 +5533,37 @@ static int pager_open_journal(Pager *pPager){ |
(SQLITE_OPEN_DELETEONCLOSE|SQLITE_OPEN_TEMP_JOURNAL): |
(SQLITE_OPEN_MAIN_JOURNAL) |
); |
- #ifdef SQLITE_ENABLE_ATOMIC_WRITE |
- rc = sqlite3JournalOpen( |
- pVfs, pPager->zJournal, pPager->jfd, flags, jrnlBufferSize(pPager) |
- ); |
- #else |
- rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, flags, 0); |
- #endif |
+ |
+ /* 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) |
+ ); |
+#else |
+ rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, flags, 0); |
+#endif |
#if defined(__APPLE__) |
- /* Set the TimeMachine exclusion metadata for the journal if it has |
- ** been set for the database. Only do this for unix-type vfs |
- ** implementations. */ |
- if( rc==SQLITE_OK && pPager->zFilename!=NULL |
- && strlen(pPager->zFilename)>0 |
- && strncmp(pVfs->zName, "unix", 4)==0 |
- && ( pVfs->zName[4]=='-' || pVfs->zName[4]=='\0' ) ){ |
- CFURLRef database = create_cfurl_from_cstring(pPager->zFilename); |
- if( CSBackupIsItemExcluded(database, NULL) ){ |
- CFURLRef journal = create_cfurl_from_cstring(pPager->zJournal); |
- /* Ignore errors from the following exclusion call. */ |
- CSBackupSetItemExcluded(journal, TRUE, FALSE); |
- CFRelease(journal); |
+ /* Set the TimeMachine exclusion metadata for the journal if it has |
+ ** been set for the database. Only do this for unix-type vfs |
+ ** implementations. */ |
+ if( rc==SQLITE_OK && pPager->zFilename!=NULL |
+ && strlen(pPager->zFilename)>0 |
+ && strncmp(pVfs->zName, "unix", 4)==0 |
+ && ( pVfs->zName[4]=='-' || pVfs->zName[4]=='\0' ) ){ |
+ CFURLRef database = create_cfurl_from_cstring(pPager->zFilename); |
+ if( CSBackupIsItemExcluded(database, NULL) ){ |
+ CFURLRef journal = create_cfurl_from_cstring(pPager->zJournal); |
+ /* Ignore errors from the following exclusion call. */ |
+ CSBackupSetItemExcluded(journal, TRUE, FALSE); |
+ CFRelease(journal); |
+ } |
+ CFRelease(database); |
} |
- CFRelease(database); |
- } |
#endif |
+ } |
} |
assert( rc!=SQLITE_OK || isOpen(pPager->jfd) ); |
} |
@@ -5304,9 +5684,9 @@ int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory){ |
** of any open savepoints as appropriate. |
*/ |
static int pager_write(PgHdr *pPg){ |
- void *pData = pPg->pData; |
Pager *pPager = pPg->pPager; |
int rc = SQLITE_OK; |
+ int inJournal; |
/* This routine is not called unless a write-transaction has already |
** been started. The journal file may or may not be open at this point. |
@@ -5317,14 +5697,8 @@ static int pager_write(PgHdr *pPg){ |
|| pPager->eState==PAGER_WRITER_DBMOD |
); |
assert( assert_pager_state(pPager) ); |
- |
- /* If an error has been previously detected, report the same error |
- ** again. This should not happen, but the check provides robustness. */ |
- if( NEVER(pPager->errCode) ) return pPager->errCode; |
- |
- /* Higher-level routines never call this function if database is not |
- ** writable. But check anyway, just for robustness. */ |
- if( NEVER(pPager->readOnly) ) return SQLITE_PERM; |
+ assert( pPager->errCode==0 ); |
+ assert( pPager->readOnly==0 ); |
CHECK_PAGE(pPg); |
@@ -5348,7 +5722,8 @@ static int pager_write(PgHdr *pPg){ |
** to the journal then we can return right away. |
*/ |
sqlite3PcacheMakeDirty(pPg); |
- if( pageInJournal(pPg) && !subjRequiresPage(pPg) ){ |
+ inJournal = pageInJournal(pPager, pPg); |
+ if( inJournal && (pPager->nSavepoint==0 || !subjRequiresPage(pPg)) ){ |
assert( !pagerUseWal(pPager) ); |
}else{ |
@@ -5356,7 +5731,7 @@ static int pager_write(PgHdr *pPg){ |
** EXCLUSIVE lock on the main database file. Write the current page to |
** the transaction journal if it is not there already. |
*/ |
- if( !pageInJournal(pPg) && !pagerUseWal(pPager) ){ |
+ if( !inJournal && !pagerUseWal(pPager) ){ |
assert( pagerUseWal(pPager)==0 ); |
if( pPg->pgno<=pPager->dbOrigSize && isOpen(pPager->jfd) ){ |
u32 cksum; |
@@ -5369,7 +5744,7 @@ static int pager_write(PgHdr *pPg){ |
assert( pPg->pgno!=PAGER_MJ_PGNO(pPager) ); |
assert( pPager->journalHdr<=pPager->journalOff ); |
- CODEC2(pPager, pData, pPg->pgno, 7, return SQLITE_NOMEM, pData2); |
+ CODEC2(pPager, pPg->pData, pPg->pgno, 7, return SQLITE_NOMEM, pData2); |
cksum = pager_cksum(pPager, (u8*)pData2); |
/* Even if an IO or diskfull error occurs while journalling the |
@@ -5421,7 +5796,7 @@ static int pager_write(PgHdr *pPg){ |
** the statement journal format differs from the standard journal format |
** in that it omits the checksums and the header. |
*/ |
- if( subjRequiresPage(pPg) ){ |
+ if( pPager->nSavepoint>0 && subjRequiresPage(pPg) ){ |
rc = subjournalPage(pPg); |
} |
} |
@@ -5435,6 +5810,97 @@ static int pager_write(PgHdr *pPg){ |
} |
/* |
+** This is a variant of sqlite3PagerWrite() that runs when the sector size |
+** is larger than the page size. SQLite makes the (reasonable) assumption that |
+** all bytes of a sector are written together by hardware. Hence, all bytes of |
+** a sector need to be journalled in case of a power loss in the middle of |
+** a write. |
+** |
+** Usually, the sector size is less than or equal to the page size, in which |
+** case pages can be individually written. This routine only runs in the exceptional |
+** case where the page size is smaller than the sector size. |
+*/ |
+static SQLITE_NOINLINE int pagerWriteLargeSector(PgHdr *pPg){ |
+ int rc = SQLITE_OK; /* Return code */ |
+ Pgno nPageCount; /* Total number of pages in database file */ |
+ Pgno pg1; /* First page of the sector pPg is located on. */ |
+ int nPage = 0; /* Number of pages starting at pg1 to journal */ |
+ int ii; /* Loop counter */ |
+ int needSync = 0; /* True if any page has PGHDR_NEED_SYNC */ |
+ Pager *pPager = pPg->pPager; /* The pager that owns pPg */ |
+ Pgno nPagePerSector = (pPager->sectorSize/pPager->pageSize); |
+ |
+ /* Set the doNotSpill NOSYNC bit to 1. This is because we cannot allow |
+ ** a journal header to be written between the pages journaled by |
+ ** this function. |
+ */ |
+ assert( !MEMDB ); |
+ assert( (pPager->doNotSpill & SPILLFLAG_NOSYNC)==0 ); |
+ pPager->doNotSpill |= SPILLFLAG_NOSYNC; |
+ |
+ /* This trick assumes that both the page-size and sector-size are |
+ ** an integer power of 2. It sets variable pg1 to the identifier |
+ ** of the first page of the sector pPg is located on. |
+ */ |
+ pg1 = ((pPg->pgno-1) & ~(nPagePerSector-1)) + 1; |
+ |
+ nPageCount = pPager->dbSize; |
+ if( pPg->pgno>nPageCount ){ |
+ nPage = (pPg->pgno - pg1)+1; |
+ }else if( (pg1+nPagePerSector-1)>nPageCount ){ |
+ nPage = nPageCount+1-pg1; |
+ }else{ |
+ nPage = nPagePerSector; |
+ } |
+ assert(nPage>0); |
+ assert(pg1<=pPg->pgno); |
+ assert((pg1+nPage)>pPg->pgno); |
+ |
+ for(ii=0; ii<nPage && rc==SQLITE_OK; ii++){ |
+ Pgno pg = pg1+ii; |
+ PgHdr *pPage; |
+ if( pg==pPg->pgno || !sqlite3BitvecTest(pPager->pInJournal, pg) ){ |
+ if( pg!=PAGER_MJ_PGNO(pPager) ){ |
+ rc = sqlite3PagerGet(pPager, pg, &pPage); |
+ if( rc==SQLITE_OK ){ |
+ rc = pager_write(pPage); |
+ if( pPage->flags&PGHDR_NEED_SYNC ){ |
+ needSync = 1; |
+ } |
+ sqlite3PagerUnrefNotNull(pPage); |
+ } |
+ } |
+ }else if( (pPage = sqlite3PagerLookup(pPager, pg))!=0 ){ |
+ if( pPage->flags&PGHDR_NEED_SYNC ){ |
+ needSync = 1; |
+ } |
+ sqlite3PagerUnrefNotNull(pPage); |
+ } |
+ } |
+ |
+ /* If the PGHDR_NEED_SYNC flag is set for any of the nPage pages |
+ ** starting at pg1, then it needs to be set for all of them. Because |
+ ** writing to any of these nPage pages may damage the others, the |
+ ** journal file must contain sync()ed copies of all of them |
+ ** before any of them can be written out to the database file. |
+ */ |
+ if( rc==SQLITE_OK && needSync ){ |
+ assert( !MEMDB ); |
+ for(ii=0; ii<nPage; ii++){ |
+ PgHdr *pPage = sqlite3PagerLookup(pPager, pg1+ii); |
+ if( pPage ){ |
+ pPage->flags |= PGHDR_NEED_SYNC; |
+ sqlite3PagerUnrefNotNull(pPage); |
+ } |
+ } |
+ } |
+ |
+ assert( (pPager->doNotSpill & SPILLFLAG_NOSYNC)!=0 ); |
+ pPager->doNotSpill &= ~SPILLFLAG_NOSYNC; |
+ return rc; |
+} |
+ |
+/* |
** Mark a data page as writeable. This routine must be called before |
** making changes to a page. The caller must check the return value |
** of this function and be careful not to change any page data unless |
@@ -5448,95 +5914,16 @@ static int pager_write(PgHdr *pPg){ |
** If an error occurs, SQLITE_NOMEM or an IO error code is returned |
** as appropriate. Otherwise, SQLITE_OK. |
*/ |
-int sqlite3PagerWrite(DbPage *pDbPage){ |
- int rc = SQLITE_OK; |
- |
- PgHdr *pPg = pDbPage; |
- Pager *pPager = pPg->pPager; |
- Pgno nPagePerSector = (pPager->sectorSize/pPager->pageSize); |
- |
- assert( pPager->eState>=PAGER_WRITER_LOCKED ); |
- assert( pPager->eState!=PAGER_ERROR ); |
- assert( assert_pager_state(pPager) ); |
- |
- if( nPagePerSector>1 ){ |
- Pgno nPageCount; /* Total number of pages in database file */ |
- Pgno pg1; /* First page of the sector pPg is located on. */ |
- int nPage = 0; /* Number of pages starting at pg1 to journal */ |
- int ii; /* Loop counter */ |
- int needSync = 0; /* True if any page has PGHDR_NEED_SYNC */ |
- |
- /* Set the doNotSyncSpill flag to 1. This is because we cannot allow |
- ** a journal header to be written between the pages journaled by |
- ** this function. |
- */ |
- assert( !MEMDB ); |
- assert( pPager->doNotSyncSpill==0 ); |
- pPager->doNotSyncSpill++; |
- |
- /* This trick assumes that both the page-size and sector-size are |
- ** an integer power of 2. It sets variable pg1 to the identifier |
- ** of the first page of the sector pPg is located on. |
- */ |
- pg1 = ((pPg->pgno-1) & ~(nPagePerSector-1)) + 1; |
- |
- nPageCount = pPager->dbSize; |
- if( pPg->pgno>nPageCount ){ |
- nPage = (pPg->pgno - pg1)+1; |
- }else if( (pg1+nPagePerSector-1)>nPageCount ){ |
- nPage = nPageCount+1-pg1; |
- }else{ |
- nPage = nPagePerSector; |
- } |
- assert(nPage>0); |
- assert(pg1<=pPg->pgno); |
- assert((pg1+nPage)>pPg->pgno); |
- |
- for(ii=0; ii<nPage && rc==SQLITE_OK; ii++){ |
- Pgno pg = pg1+ii; |
- PgHdr *pPage; |
- if( pg==pPg->pgno || !sqlite3BitvecTest(pPager->pInJournal, pg) ){ |
- if( pg!=PAGER_MJ_PGNO(pPager) ){ |
- rc = sqlite3PagerGet(pPager, pg, &pPage); |
- if( rc==SQLITE_OK ){ |
- rc = pager_write(pPage); |
- if( pPage->flags&PGHDR_NEED_SYNC ){ |
- needSync = 1; |
- } |
- sqlite3PagerUnref(pPage); |
- } |
- } |
- }else if( (pPage = pager_lookup(pPager, pg))!=0 ){ |
- if( pPage->flags&PGHDR_NEED_SYNC ){ |
- needSync = 1; |
- } |
- sqlite3PagerUnref(pPage); |
- } |
- } |
- |
- /* If the PGHDR_NEED_SYNC flag is set for any of the nPage pages |
- ** starting at pg1, then it needs to be set for all of them. Because |
- ** writing to any of these nPage pages may damage the others, the |
- ** journal file must contain sync()ed copies of all of them |
- ** before any of them can be written out to the database file. |
- */ |
- if( rc==SQLITE_OK && needSync ){ |
- assert( !MEMDB ); |
- for(ii=0; ii<nPage; ii++){ |
- PgHdr *pPage = pager_lookup(pPager, pg1+ii); |
- if( pPage ){ |
- pPage->flags |= PGHDR_NEED_SYNC; |
- sqlite3PagerUnref(pPage); |
- } |
- } |
- } |
- |
- assert( pPager->doNotSyncSpill==1 ); |
- pPager->doNotSyncSpill--; |
+int sqlite3PagerWrite(PgHdr *pPg){ |
+ assert( (pPg->flags & PGHDR_MMAP)==0 ); |
+ assert( pPg->pPager->eState>=PAGER_WRITER_LOCKED ); |
+ assert( pPg->pPager->eState!=PAGER_ERROR ); |
+ assert( assert_pager_state(pPg->pPager) ); |
+ if( pPg->pPager->sectorSize > (u32)pPg->pPager->pageSize ){ |
+ return pagerWriteLargeSector(pPg); |
}else{ |
- rc = pager_write(pDbPage); |
+ return pager_write(pPg); |
} |
- return rc; |
} |
/* |
@@ -5622,7 +6009,7 @@ static int pager_incr_changecounter(Pager *pPager, int isDirectMode){ |
# define DIRECT_MODE isDirectMode |
#endif |
- if( !pPager->changeCountDone && pPager->dbSize>0 ){ |
+ if( !pPager->changeCountDone && ALWAYS(pPager->dbSize>0) ){ |
PgHdr *pPgHdr; /* Reference to page 1 */ |
assert( !pPager->tempFile && isOpen(pPager->fd) ); |
@@ -5651,8 +6038,14 @@ static int pager_incr_changecounter(Pager *pPager, int isDirectMode){ |
CODEC2(pPager, pPgHdr->pData, 1, 6, rc=SQLITE_NOMEM, zBuf); |
if( rc==SQLITE_OK ){ |
rc = sqlite3OsWrite(pPager->fd, zBuf, pPager->pageSize, 0); |
+ pPager->aStat[PAGER_STAT_WRITE]++; |
} |
if( rc==SQLITE_OK ){ |
+ /* Update the pager's copy of the change-counter. Otherwise, the |
+ ** next time a read transaction is opened the cache will be |
+ ** flushed (as the change-counter values will not match). */ |
+ const void *pCopy = (const void *)&((const char *)zBuf)[24]; |
+ memcpy(&pPager->dbFileVers, pCopy, sizeof(pPager->dbFileVers)); |
pPager->changeCountDone = 1; |
} |
}else{ |
@@ -5673,14 +6066,17 @@ static int pager_incr_changecounter(Pager *pPager, int isDirectMode){ |
** If successful, or if called on a pager for which it is a no-op, this |
** function returns SQLITE_OK. Otherwise, an IO error code is returned. |
*/ |
-int sqlite3PagerSync(Pager *pPager){ |
+int sqlite3PagerSync(Pager *pPager, const char *zMaster){ |
int rc = SQLITE_OK; |
- if( !pPager->noSync ){ |
+ |
+ if( isOpen(pPager->fd) ){ |
+ void *pArg = (void*)zMaster; |
+ rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_SYNC, pArg); |
+ if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK; |
+ } |
+ if( rc==SQLITE_OK && !pPager->noSync ){ |
assert( !MEMDB ); |
rc = sqlite3OsSync(pPager->fd, pPager->syncFlags); |
- }else if( isOpen(pPager->fd) ){ |
- assert( !MEMDB ); |
- sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_SYNC_OMITTED, (void *)&rc); |
} |
return rc; |
} |
@@ -5775,11 +6171,9 @@ int sqlite3PagerCommitPhaseOne( |
pList = pPageOne; |
pList->pDirty = 0; |
} |
- assert( pList!=0 || rc!=SQLITE_OK ); |
- if( pList ){ |
- rc = pagerWalFrames(pPager, pList, pPager->dbSize, 1, |
- (pPager->fullSync ? pPager->syncFlags : 0) |
- ); |
+ assert( rc==SQLITE_OK ); |
+ if( ALWAYS(pList) ){ |
+ rc = pagerWalFrames(pPager, pList, pPager->dbSize, 1); |
} |
sqlite3PagerUnref(pPageOne); |
if( rc==SQLITE_OK ){ |
@@ -5838,38 +6232,6 @@ int sqlite3PagerCommitPhaseOne( |
#endif |
if( rc!=SQLITE_OK ) goto commit_phase_one_exit; |
- /* If this transaction has made the database smaller, then all pages |
- ** being discarded by the truncation must be written to the journal |
- ** file. This can only happen in auto-vacuum mode. |
- ** |
- ** Before reading the pages with page numbers larger than the |
- ** current value of Pager.dbSize, set dbSize back to the value |
- ** that it took at the start of the transaction. Otherwise, the |
- ** calls to sqlite3PagerGet() return zeroed pages instead of |
- ** reading data from the database file. |
- */ |
- #ifndef SQLITE_OMIT_AUTOVACUUM |
- if( pPager->dbSize<pPager->dbOrigSize |
- && pPager->journalMode!=PAGER_JOURNALMODE_OFF |
- ){ |
- Pgno i; /* Iterator variable */ |
- const Pgno iSkip = PAGER_MJ_PGNO(pPager); /* Pending lock page */ |
- const Pgno dbSize = pPager->dbSize; /* Database image size */ |
- pPager->dbSize = pPager->dbOrigSize; |
- for( i=dbSize+1; i<=pPager->dbOrigSize; i++ ){ |
- if( !sqlite3BitvecTest(pPager->pInJournal, i) && i!=iSkip ){ |
- PgHdr *pPage; /* Page to journal */ |
- rc = sqlite3PagerGet(pPager, i, &pPage); |
- if( rc!=SQLITE_OK ) goto commit_phase_one_exit; |
- rc = sqlite3PagerWrite(pPage); |
- sqlite3PagerUnref(pPage); |
- if( rc!=SQLITE_OK ) goto commit_phase_one_exit; |
- } |
- } |
- pPager->dbSize = dbSize; |
- } |
- #endif |
- |
/* Write the master journal name into the journal file. If a master |
** journal file name has already been written to the journal file, |
** or if zMaster is NULL (no master journal), then this call is a no-op. |
@@ -5897,11 +6259,14 @@ int sqlite3PagerCommitPhaseOne( |
goto commit_phase_one_exit; |
} |
sqlite3PcacheCleanAll(pPager->pPCache); |
- |
- /* If the file on disk is not the same size as the database image, |
- ** then use pager_truncate to grow or shrink the file here. |
- */ |
- if( pPager->dbSize!=pPager->dbFileSize ){ |
+ |
+ /* If the file on disk is smaller than the database image, use |
+ ** pager_truncate to grow the file here. This can happen if the database |
+ ** image was extended as part of the current transaction and then the |
+ ** last page in the db image moved to the free-list. In this case the |
+ ** last page is never written out to disk, leaving the database file |
+ ** undersized. Fix this now if it is the case. */ |
+ if( pPager->dbSize>pPager->dbFileSize ){ |
Pgno nNew = pPager->dbSize - (pPager->dbSize==PAGER_MJ_PGNO(pPager)); |
assert( pPager->eState==PAGER_WRITER_DBMOD ); |
rc = pager_truncate(pPager, nNew); |
@@ -5910,7 +6275,7 @@ int sqlite3PagerCommitPhaseOne( |
/* Finally, sync the database file. */ |
if( !noSync ){ |
- rc = sqlite3PagerSync(pPager); |
+ rc = sqlite3PagerSync(pPager, zMaster); |
} |
IOTRACE(("DBSYNC %p\n", pPager)) |
} |
@@ -5974,7 +6339,7 @@ int sqlite3PagerCommitPhaseTwo(Pager *pPager){ |
} |
PAGERTRACE(("COMMIT %d\n", PAGERID(pPager))); |
- rc = pager_end_transaction(pPager, pPager->setMaster); |
+ rc = pager_end_transaction(pPager, pPager->setMaster, 1); |
return pager_error(pPager, rc); |
} |
@@ -6019,11 +6384,11 @@ int sqlite3PagerRollback(Pager *pPager){ |
if( pagerUseWal(pPager) ){ |
int rc2; |
rc = sqlite3PagerSavepoint(pPager, SAVEPOINT_ROLLBACK, -1); |
- rc2 = pager_end_transaction(pPager, pPager->setMaster); |
+ rc2 = pager_end_transaction(pPager, pPager->setMaster, 0); |
if( rc==SQLITE_OK ) rc = rc2; |
}else if( !isOpen(pPager->jfd) || pPager->eState==PAGER_WRITER_LOCKED ){ |
int eState = pPager->eState; |
- rc = pager_end_transaction(pPager, 0); |
+ rc = pager_end_transaction(pPager, 0, 0); |
if( !MEMDB && eState>PAGER_WRITER_LOCKED ){ |
/* This can happen using journal_mode=off. Move the pager to the error |
** state to indicate that the contents of the cache may not be trusted. |
@@ -6038,7 +6403,10 @@ int sqlite3PagerRollback(Pager *pPager){ |
} |
assert( pPager->eState==PAGER_READER || rc!=SQLITE_OK ); |
- assert( rc==SQLITE_OK || rc==SQLITE_FULL || (rc&0xFF)==SQLITE_IOERR ); |
+ assert( rc==SQLITE_OK || rc==SQLITE_FULL || rc==SQLITE_CORRUPT |
+ || rc==SQLITE_NOMEM || (rc&0xFF)==SQLITE_IOERR |
+ || rc==SQLITE_CANTOPEN |
+ ); |
/* If an error occurs during a ROLLBACK, we can no longer trust the pager |
** cache. So call pager_error() on the way out to make any error persistent. |
@@ -6092,16 +6460,40 @@ int *sqlite3PagerStats(Pager *pPager){ |
a[3] = pPager->eState==PAGER_OPEN ? -1 : (int) pPager->dbSize; |
a[4] = pPager->eState; |
a[5] = pPager->errCode; |
- a[6] = pPager->nHit; |
- a[7] = pPager->nMiss; |
+ a[6] = pPager->aStat[PAGER_STAT_HIT]; |
+ a[7] = pPager->aStat[PAGER_STAT_MISS]; |
a[8] = 0; /* Used to be pPager->nOvfl */ |
a[9] = pPager->nRead; |
- a[10] = pPager->nWrite; |
+ a[10] = pPager->aStat[PAGER_STAT_WRITE]; |
return a; |
} |
#endif |
/* |
+** Parameter eStat must be either SQLITE_DBSTATUS_CACHE_HIT or |
+** SQLITE_DBSTATUS_CACHE_MISS. Before returning, *pnVal is incremented by the |
+** current cache hit or miss count, according to the value of eStat. If the |
+** reset parameter is non-zero, the cache hit or miss count is zeroed before |
+** returning. |
+*/ |
+void sqlite3PagerCacheStat(Pager *pPager, int eStat, int reset, int *pnVal){ |
+ |
+ assert( eStat==SQLITE_DBSTATUS_CACHE_HIT |
+ || eStat==SQLITE_DBSTATUS_CACHE_MISS |
+ || eStat==SQLITE_DBSTATUS_CACHE_WRITE |
+ ); |
+ |
+ assert( SQLITE_DBSTATUS_CACHE_HIT+1==SQLITE_DBSTATUS_CACHE_MISS ); |
+ assert( SQLITE_DBSTATUS_CACHE_HIT+2==SQLITE_DBSTATUS_CACHE_WRITE ); |
+ assert( PAGER_STAT_HIT==0 && PAGER_STAT_MISS==1 && PAGER_STAT_WRITE==2 ); |
+ |
+ *pnVal += pPager->aStat[eStat - SQLITE_DBSTATUS_CACHE_HIT]; |
+ if( reset ){ |
+ pPager->aStat[eStat - SQLITE_DBSTATUS_CACHE_HIT] = 0; |
+ } |
+} |
+ |
+/* |
** Return true if this is an in-memory pager. |
*/ |
int sqlite3PagerIsMemdb(Pager *pPager){ |
@@ -6246,9 +6638,16 @@ int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){ |
/* |
** Return the full pathname of the database file. |
+** |
+** Except, if the pager is in-memory only, then return an empty string if |
+** nullIfMemDb is true. This routine is called with nullIfMemDb==1 when |
+** used to report the filename to the user, for compatibility with legacy |
+** behavior. But when the Btree needs to know the filename for matching to |
+** shared cache, it uses nullIfMemDb==0 so that in-memory databases can |
+** participate in shared-cache. |
*/ |
-const char *sqlite3PagerFilename(Pager *pPager){ |
- return pPager->zFilename; |
+const char *sqlite3PagerFilename(Pager *pPager, int nullIfMemDb){ |
+ return (nullIfMemDb && pPager->memDb) ? "" : pPager->zFilename; |
} |
/* |
@@ -6303,7 +6702,27 @@ void sqlite3PagerSetCodec( |
void *sqlite3PagerGetCodec(Pager *pPager){ |
return pPager->pCodec; |
} |
-#endif |
+ |
+/* |
+** This function is called by the wal module when writing page content |
+** into the log file. |
+** |
+** This function returns a pointer to a buffer containing the encrypted |
+** page content. If a malloc fails, this function may return NULL. |
+*/ |
+void *sqlite3PagerCodec(PgHdr *pPg){ |
+ void *aData = 0; |
+ CODEC2(pPg->pPager, pPg->pData, pPg->pgno, 6, return 0, aData); |
+ return aData; |
+} |
+ |
+/* |
+** Return the current pager state |
+*/ |
+int sqlite3PagerState(Pager *pPager){ |
+ return pPager->eState; |
+} |
+#endif /* SQLITE_HAS_CODEC */ |
#ifndef SQLITE_OMIT_AUTOVACUUM |
/* |
@@ -6389,7 +6808,8 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){ |
*/ |
if( (pPg->flags&PGHDR_NEED_SYNC) && !isCommit ){ |
needSyncPgno = pPg->pgno; |
- assert( pageInJournal(pPg) || pPg->pgno>pPager->dbOrigSize ); |
+ assert( pPager->journalMode==PAGER_JOURNALMODE_OFF || |
+ pageInJournal(pPager, pPg) || pPg->pgno>pPager->dbOrigSize ); |
assert( pPg->flags&PGHDR_DIRTY ); |
} |
@@ -6399,7 +6819,7 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){ |
** for the page moved there. |
*/ |
pPg->flags &= ~PGHDR_NEED_SYNC; |
- pPgOld = pager_lookup(pPager, pgno); |
+ pPgOld = sqlite3PagerLookup(pPager, pgno); |
assert( !pPgOld || pPgOld->nRef==1 ); |
if( pPgOld ){ |
pPg->flags |= (pPgOld->flags&PGHDR_NEED_SYNC); |
@@ -6423,7 +6843,7 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){ |
if( MEMDB ){ |
assert( pPgOld ); |
sqlite3PcacheMove(pPgOld, origPgno); |
- sqlite3PagerUnref(pPgOld); |
+ sqlite3PagerUnrefNotNull(pPgOld); |
} |
if( needSyncPgno ){ |
@@ -6452,7 +6872,7 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){ |
} |
pPgHdr->flags |= PGHDR_NEED_SYNC; |
sqlite3PcacheMakeDirty(pPgHdr); |
- sqlite3PagerUnref(pPgHdr); |
+ sqlite3PagerUnrefNotNull(pPgHdr); |
} |
return SQLITE_OK; |
@@ -6639,6 +7059,7 @@ int sqlite3PagerOkToChangeJournalMode(Pager *pPager){ |
i64 sqlite3PagerJournalSizeLimit(Pager *pPager, i64 iLimit){ |
if( iLimit>=-1 ){ |
pPager->journalSizeLimit = iLimit; |
+ sqlite3WalLimit(pPager->pWal, iLimit); |
} |
return pPager->journalSizeLimit; |
} |
@@ -6653,6 +7074,15 @@ sqlite3_backup **sqlite3PagerBackupPtr(Pager *pPager){ |
return &pPager->pBackup; |
} |
+#ifndef SQLITE_OMIT_VACUUM |
+/* |
+** 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); |
+} |
+#endif |
+ |
#ifndef SQLITE_OMIT_WAL |
/* |
** This function is called when the user invokes "PRAGMA wal_checkpoint", |
@@ -6714,7 +7144,7 @@ static int pagerOpenWal(Pager *pPager){ |
int rc = SQLITE_OK; |
assert( pPager->pWal==0 && pPager->tempFile==0 ); |
- assert( pPager->eLock==SHARED_LOCK || pPager->eLock==EXCLUSIVE_LOCK || pPager->noReadlock); |
+ assert( pPager->eLock==SHARED_LOCK || pPager->eLock==EXCLUSIVE_LOCK ); |
/* If the pager is already in exclusive-mode, the WAL module will use |
** heap-memory for the wal-index instead of the VFS shared-memory |
@@ -6729,10 +7159,12 @@ static int pagerOpenWal(Pager *pPager){ |
** (e.g. due to malloc() failure), return an error code. |
*/ |
if( rc==SQLITE_OK ){ |
- rc = sqlite3WalOpen(pPager->pVfs, |
- pPager->fd, pPager->zWal, pPager->exclusiveMode, &pPager->pWal |
+ rc = sqlite3WalOpen(pPager->pVfs, |
+ pPager->fd, pPager->zWal, pPager->exclusiveMode, |
+ pPager->journalSizeLimit, &pPager->pWal |
); |
} |
+ pagerFixMaplimit(pPager); |
return rc; |
} |
@@ -6823,26 +7255,26 @@ int sqlite3PagerCloseWal(Pager *pPager){ |
rc = sqlite3WalClose(pPager->pWal, pPager->ckptSyncFlags, |
pPager->pageSize, (u8*)pPager->pTmpSpace); |
pPager->pWal = 0; |
+ pagerFixMaplimit(pPager); |
} |
} |
return rc; |
} |
-#ifdef SQLITE_HAS_CODEC |
+#endif /* !SQLITE_OMIT_WAL */ |
+ |
+#ifdef SQLITE_ENABLE_ZIPVFS |
/* |
-** This function is called by the wal module when writing page content |
-** into the log file. |
-** |
-** This function returns a pointer to a buffer containing the encrypted |
-** page content. If a malloc fails, this function may return NULL. |
+** A read-lock must be held on the pager when this function is called. If |
+** the pager is in WAL mode and the WAL file currently contains one or more |
+** frames, return the size in bytes of the page images stored within the |
+** WAL frames. Otherwise, if this is not a WAL database or the WAL file |
+** is empty, return 0. |
*/ |
-void *sqlite3PagerCodec(PgHdr *pPg){ |
- void *aData = 0; |
- CODEC2(pPg->pPager, pPg->pData, pPg->pgno, 6, return 0, aData); |
- return aData; |
+int sqlite3PagerWalFramesize(Pager *pPager){ |
+ assert( pPager->eState>=PAGER_READER ); |
+ return sqlite3WalFramesize(pPager->pWal); |
} |
-#endif /* SQLITE_HAS_CODEC */ |
- |
-#endif /* !SQLITE_OMIT_WAL */ |
+#endif |
#endif /* SQLITE_OMIT_DISKIO */ |