pager.c
来自「sqlite最新源码」· C语言 代码 · 共 1,620 行 · 第 1/5 页
C
1,620 行
} }else if( pPager->journalMode==PAGER_JOURNALMODE_TRUNCATE ){ rc = sqlite3OsTruncate(pPager->jfd, 0); pPager->journalOff = 0; pPager->journalStarted = 0; }else if( pPager->exclusiveMode || pPager->journalMode==PAGER_JOURNALMODE_PERSIST ){ rc = zeroJournalHdr(pPager, hasMaster); pager_error(pPager, rc); pPager->journalOff = 0; pPager->journalStarted = 0; }else{ assert( pPager->journalMode==PAGER_JOURNALMODE_DELETE || rc ); sqlite3OsClose(pPager->jfd); if( rc==SQLITE_OK && !pPager->tempFile ){ rc = sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0); } }#ifdef SQLITE_CHECK_PAGES sqlite3PcacheIterateDirty(pPager->pPCache, pager_set_pagehash);#endif sqlite3PcacheCleanAll(pPager->pPCache); sqlite3BitvecDestroy(pPager->pInJournal); pPager->pInJournal = 0; pPager->nRec = 0; } if( !pPager->exclusiveMode ){ rc2 = osUnlock(pPager->fd, SHARED_LOCK); pPager->state = PAGER_SHARED; pPager->changeCountDone = 0; }else if( pPager->state==PAGER_SYNCED ){ pPager->state = PAGER_EXCLUSIVE; } pPager->setMaster = 0; pPager->needSync = 0; pPager->dbModified = 0; /* TODO: Is this optimal? Why is the db size invalidated here ** when the database file is not unlocked? */ pPager->dbOrigSize = 0; sqlite3PcacheTruncate(pPager->pPCache, pPager->dbSize); if( !MEMDB ){ pPager->dbSizeValid = 0; } return (rc==SQLITE_OK?rc2:rc);}/*** Parameter aData must point to a buffer of pPager->pageSize bytes** of data. Compute and return a checksum based ont the contents of the ** page of data and the current value of pPager->cksumInit.**** This is not a real checksum. It is really just the sum of the ** random initial value (pPager->cksumInit) and every 200th byte** of the page data, starting with byte offset (pPager->pageSize%200).** Each byte is interpreted as an 8-bit unsigned integer.**** Changing the formula used to compute this checksum results in an** incompatible journal file format.**** If journal corruption occurs due to a power failure, the most likely ** scenario is that one end or the other of the record will be changed. ** It is much less likely that the two ends of the journal record will be** correct and the middle be corrupt. Thus, this "checksum" scheme,** though fast and simple, catches the mostly likely kind of corruption.*/static u32 pager_cksum(Pager *pPager, const u8 *aData){ u32 cksum = pPager->cksumInit; /* Checksum value to return */ int i = pPager->pageSize-200; /* Loop counter */ while( i>0 ){ cksum += aData[i]; i -= 200; } return cksum;}/*** Read a single page from either the journal file (if isMainJrnl==1) or** from the sub-journal (if isMainJrnl==0) and playback that page.** The page begins at offset *pOffset into the file. The *pOffset** value is increased to the start of the next page in the journal.**** The isMainJrnl flag is true if this is the main rollback journal and** false for the statement journal. The main rollback journal uses** checksums - the statement journal does not.**** If the page number of the page record read from the (sub-)journal file** is greater than the current value of Pager.dbSize, then playback is** skipped and SQLITE_OK is returned.**** If pDone is not NULL, then it is a record of pages that have already** been played back. If the page at *pOffset has already been played back** (if the corresponding pDone bit is set) then skip the playback.** Make sure the pDone bit corresponding to the *pOffset page is set** prior to returning.**** If the page record is successfully read from the (sub-)journal file** and played back, then SQLITE_OK is returned. If an IO error occurs** while reading the record from the (sub-)journal file or while writing** to the database file, then the IO error code is returned. If data** is successfully read from the (sub-)journal file but appears to be** corrupted, SQLITE_DONE is returned. Data is considered corrupted in** two circumstances:** ** * If the record page-number is illegal (0 or PAGER_MJ_PGNO), or** * If the record is being rolled back from the main journal file** and the checksum field does not match the record content.**** Neither of these two scenarios are possible during a savepoint rollback.**** If this is a savepoint rollback, then memory may have to be dynamically** allocated by this function. If this is the case and an allocation fails,** SQLITE_NOMEM is returned.*/static int pager_playback_one_page( Pager *pPager, /* The pager being played back */ int isMainJrnl, /* 1 -> main journal. 0 -> sub-journal. */ int isUnsync, /* True if reading from unsynced main journal */ i64 *pOffset, /* Offset of record to playback */ int isSavepnt, /* True for a savepoint rollback */ Bitvec *pDone /* Bitvec of pages already played back */){ int rc; PgHdr *pPg; /* An existing page in the cache */ Pgno pgno; /* The page number of a page in journal */ u32 cksum; /* Checksum used for sanity checking */ u8 *aData; /* Temporary storage for the page */ sqlite3_file *jfd; /* The file descriptor for the journal file */ assert( (isMainJrnl&~1)==0 ); /* isMainJrnl is 0 or 1 */ assert( (isSavepnt&~1)==0 ); /* isSavepnt is 0 or 1 */ assert( isMainJrnl || pDone ); /* pDone always used on sub-journals */ assert( isSavepnt || pDone==0 ); /* pDone never used on non-savepoint */ aData = (u8*)pPager->pTmpSpace; assert( aData ); /* Temp storage must have already been allocated */ /* Read the page number and page data from the journal or sub-journal ** file. Return an error code to the caller if an IO error occurs. */ jfd = isMainJrnl ? pPager->jfd : pPager->sjfd; rc = read32bits(jfd, *pOffset, &pgno); if( rc!=SQLITE_OK ) return rc; rc = sqlite3OsRead(jfd, aData, pPager->pageSize, (*pOffset)+4); if( rc!=SQLITE_OK ) return rc; *pOffset += pPager->pageSize + 4 + isMainJrnl*4; /* Sanity checking on the page. This is more important that I originally ** thought. If a power failure occurs while the journal is being written, ** it could cause invalid data to be written into the journal. We need to ** detect this invalid data (with high probability) and ignore it. */ if( pgno==0 || pgno==PAGER_MJ_PGNO(pPager) ){ assert( !isSavepnt ); return SQLITE_DONE; } if( pgno>(Pgno)pPager->dbSize || sqlite3BitvecTest(pDone, pgno) ){ return SQLITE_OK; } if( isMainJrnl ){ rc = read32bits(jfd, (*pOffset)-4, &cksum); if( rc ) return rc; if( !isSavepnt && pager_cksum(pPager, aData)!=cksum ){ return SQLITE_DONE; } } if( pDone && (rc = sqlite3BitvecSet(pDone, pgno))!=SQLITE_OK ){ return rc; } assert( pPager->state==PAGER_RESERVED || pPager->state>=PAGER_EXCLUSIVE ); /* If the pager is in RESERVED state, then there must be a copy of this ** page in the pager cache. In this case just update the pager cache, ** not the database file. The page is left marked dirty in this case. ** ** An exception to the above rule: If the database is in no-sync mode ** and a page is moved during an incremental vacuum then the page may ** not be in the pager cache. Later: if a malloc() or IO error occurs ** during a Movepage() call, then the page may not be in the cache ** either. So the condition described in the above paragraph is not ** assert()able. ** ** If in EXCLUSIVE state, then we update the pager cache if it exists ** and the main file. The page is then marked not dirty. ** ** Ticket #1171: The statement journal might contain page content that is ** different from the page content at the start of the transaction. ** This occurs when a page is changed prior to the start of a statement ** then changed again within the statement. When rolling back such a ** statement we must not write to the original database unless we know ** for certain that original page contents are synced into the main rollback ** journal. Otherwise, a power loss might leave modified data in the ** database file without an entry in the rollback journal that can ** restore the database to its original form. Two conditions must be ** met before writing to the database files. (1) the database must be ** locked. (2) we know that the original page content is fully synced ** in the main journal either because the page is not in cache or else ** the page is marked as needSync==0. ** ** 2008-04-14: When attempting to vacuum a corrupt database file, it ** is possible to fail a statement on a database that does not yet exist. ** Do not attempt to write if database file has never been opened. */ pPg = pager_lookup(pPager, pgno); PAGERTRACE(("PLAYBACK %d page %d hash(%08x) %s\n", PAGERID(pPager), pgno, pager_datahash(pPager->pageSize, aData), (isMainJrnl?"main-journal":"sub-journal") )); if( (pPager->state>=PAGER_EXCLUSIVE) && (pPg==0 || 0==(pPg->flags&PGHDR_NEED_SYNC)) && isOpen(pPager->fd) && !isUnsync ){ i64 ofst = (pgno-1)*(i64)pPager->pageSize; rc = sqlite3OsWrite(pPager->fd, aData, pPager->pageSize, ofst); if( pgno>pPager->dbFileSize ){ pPager->dbFileSize = pgno; } sqlite3BackupUpdate(pPager->pBackup, pgno, aData); }else if( !isMainJrnl && pPg==0 ){ /* If this is a rollback of a savepoint and data was not written to ** the database and the page is not in-memory, there is a potential ** problem. When the page is next fetched by the b-tree layer, it ** will be read from the database file, which may or may not be ** current. ** ** There are a couple of different ways this can happen. All are quite ** obscure. When running in synchronous mode, this can only happen ** if the page is on the free-list at the start of the transaction, then ** populated, then moved using sqlite3PagerMovepage(). ** ** The solution is to add an in-memory page to the cache containing ** the data just read from the sub-journal. Mark the page as dirty ** and if the pager requires a journal-sync, then mark the page as ** requiring a journal-sync before it is written. */ assert( isSavepnt ); if( (rc = sqlite3PagerAcquire(pPager, pgno, &pPg, 1))!=SQLITE_OK ){ return rc; } pPg->flags &= ~PGHDR_NEED_READ; sqlite3PcacheMakeDirty(pPg); } if( pPg ){ /* No page should ever be explicitly rolled back that is in use, except ** for page 1 which is held in use in order to keep the lock on the ** database active. However such a page may be rolled back as a result ** of an internal error resulting in an automatic call to ** sqlite3PagerRollback(). */ void *pData; pData = pPg->pData; memcpy(pData, aData, pPager->pageSize); if( pPager->xReiniter ){ pPager->xReiniter(pPg); } if( isMainJrnl && (!isSavepnt || *pOffset<=pPager->journalHdr) ){ /* If the contents of this page were just restored from the main ** journal file, then its content must be as they were when the ** transaction was first opened. In this case we can mark the page ** as clean, since there will be no need to write it out to the. ** ** There is one exception to this rule. If the page is being rolled ** back as part of a savepoint (or statement) rollback from an ** unsynced portion of the main journal file, then it is not safe ** to mark the page as clean. This is because marking the page as ** clean will clear the PGHDR_NEED_SYNC flag. Since the page is ** already in the journal file (recorded in Pager.pInJournal) and ** the PGHDR_NEED_SYNC flag is cleared, if the page is written to ** again within this transaction, it will be marked as dirty but ** the PGHDR_NEED_SYNC flag will not be set. It could then potentially ** be written out into the database file before its journal file ** segment is synced. If a crash occurs during or following this, ** database corruption may ensue. */ sqlite3PcacheMakeClean(pPg); }#ifdef SQLITE_CHECK_PAGES pPg->pageHash = pager_pagehash(pPg);#endif /* If this was page 1, then restore the value of Pager.dbFileVers. ** Do this before any decoding. */ if( pgno==1 ){ memcpy(&pPager->dbFileVers, &((u8*)pData)[24],sizeof(pPager->dbFileVers)); } /* Decode the page just read from disk */ CODEC1(pPager, pData, pPg->pgno, 3); sqlite3PcacheRelease(pPg); } return rc;}#if !defined(NDEBUG) || defined(SQLITE_COVERAGE_TEST)/*** This routine looks ahead into the main journal file and determines** whether or not the next record (the record that begins at file** offset pPager->journalOff) is a well-formed page record consisting** of a valid page number, pPage->pageSize bytes of content, followed** by a valid checksum.**** The pager never needs to know this in order to do its job. This** routine is only used from with assert() and testcase() macros.*/static int pagerNextJournalPageIsValid(Pager *pPager){ Pgno pgno; /* The page number of the page */ u32 cksum; /* The page checksum */ int rc; /* Return code from read operations */ sqlite3_file *fd; /* The file descriptor from which we are reading */ u8 *aData; /* Content of the page */ /* Read the page number header */ fd = pPager->jfd; rc = read32bits(fd, pPager->journalOff, &pgno); if( rc!=SQLITE_OK ){ return 0; } /*NO_TEST*/ if( pgno==0 || pgno==PAGER_MJ_PGNO(pPager) ){ return 0; } /*NO_TEST*/ if( pgno>(Pgno)pPager->dbSize ){ return 0; }
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?