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