📄 pager.c
字号:
}/*** Make every page in the cache agree with what is on disk. In other words,** reread the disk to reset the state of the cache.**** This routine is called after a rollback in which some of the dirty cache** pages had never been written out to disk. We need to roll back the** cache content and the easiest way to do that is to reread the old content** back from the disk.*/static int pager_reload_cache(Pager *pPager){ PgHdr *pPg; int rc = SQLITE_OK; for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){ char zBuf[SQLITE_MAX_PAGE_SIZE]; if( !pPg->dirty ) continue; if( (int)pPg->pgno <= pPager->origDbSize ){ sqlite3OsSeek(&pPager->fd, pPager->pageSize*(i64)(pPg->pgno-1)); rc = sqlite3OsRead(&pPager->fd, zBuf, pPager->pageSize); TRACE3("REFETCH %d page %d\n", PAGERID(pPager), pPg->pgno); if( rc ) break; CODEC(pPager, zBuf, pPg->pgno, 2); }else{ memset(zBuf, 0, pPager->pageSize); } if( pPg->nRef==0 || memcmp(zBuf, PGHDR_TO_DATA(pPg), pPager->pageSize) ){ memcpy(PGHDR_TO_DATA(pPg), zBuf, pPager->pageSize); if( pPager->xReiniter ){ pPager->xReiniter(PGHDR_TO_DATA(pPg), pPager->pageSize); }else{ memset(PGHDR_TO_EXTRA(pPg, pPager), 0, pPager->nExtra); } } pPg->needSync = 0; pPg->dirty = 0;#ifdef SQLITE_CHECK_PAGES pPg->pageHash = pager_pagehash(pPg);#endif } return rc;}/*** Truncate the main file of the given pager to the number of pages** indicated.*/static int pager_truncate(Pager *pPager, int nPage){ assert( pPager->state>=PAGER_EXCLUSIVE ); return sqlite3OsTruncate(&pPager->fd, pPager->pageSize*(i64)nPage);}/*** Playback the journal and thus restore the database file to** the state it was in before we started making changes. **** The journal file format is as follows: **** (1) 8 byte prefix. A copy of aJournalMagic[].** (2) 4 byte big-endian integer which is the number of valid page records** in the journal. If this value is 0xffffffff, then compute the** number of page records from the journal size.** (3) 4 byte big-endian integer which is the initial value for the ** sanity checksum.** (4) 4 byte integer which is the number of pages to truncate the** database to during a rollback.** (5) 4 byte integer which is the number of bytes in the master journal** name. The value may be zero (indicate that there is no master** journal.)** (6) N bytes of the master journal name. The name will be nul-terminated** and might be shorter than the value read from (5). If the first byte** of the name is \000 then there is no master journal. The master** journal name is stored in UTF-8.** (7) Zero or more pages instances, each as follows:** + 4 byte page number.** + pPager->pageSize bytes of data.** + 4 byte checksum**** When we speak of the journal header, we mean the first 6 items above.** Each entry in the journal is an instance of the 7th item.**** Call the value from the second bullet "nRec". nRec is the number of** valid page entries in the journal. In most cases, you can compute the** value of nRec from the size of the journal file. But if a power** failure occurred while the journal was being written, it could be the** case that the size of the journal file had already been increased but** the extra entries had not yet made it safely to disk. In such a case,** the value of nRec computed from the file size would be too large. For** that reason, we always use the nRec value in the header.**** If the nRec value is 0xffffffff it means that nRec should be computed** from the file size. This value is used when the user selects the** no-sync option for the journal. A power failure could lead to corruption** in this case. But for things like temporary table (which will be** deleted when the power is restored) we don't care. **** If the file opened as the journal file is not a well-formed** journal file then all pages up to the first corrupted page are rolled** back (or no pages if the journal header is corrupted). The journal file** is then deleted and SQLITE_OK returned, just as if no corruption had** been encountered.**** If an I/O or malloc() error occurs, the journal-file is not deleted** and an error code is returned.*/static int pager_playback(Pager *pPager){ i64 szJ; /* Size of the journal file in bytes */ u32 nRec; /* Number of Records in the journal */ int i; /* Loop counter */ Pgno mxPg = 0; /* Size of the original file in pages */ int rc; /* Result code of a subroutine */ char *zMaster = 0; /* Name of master journal file if any */ /* Figure out how many records are in the journal. Abort early if ** the journal is empty. */ assert( pPager->journalOpen ); rc = sqlite3OsFileSize(&pPager->jfd, &szJ); if( rc!=SQLITE_OK ){ goto end_playback; } /* Read the master journal name from the journal, if it is present. ** If a master journal file name is specified, but the file is not ** present on disk, then the journal is not hot and does not need to be ** played back. */ rc = readMasterJournal(&pPager->jfd, &zMaster); assert( rc!=SQLITE_DONE ); if( rc!=SQLITE_OK || (zMaster && !sqlite3OsFileExists(zMaster)) ){ sqliteFree(zMaster); zMaster = 0; if( rc==SQLITE_DONE ) rc = SQLITE_OK; goto end_playback; } sqlite3OsSeek(&pPager->jfd, 0); pPager->journalOff = 0; /* This loop terminates either when the readJournalHdr() call returns ** SQLITE_DONE or an IO error occurs. */ while( 1 ){ /* Read the next journal header from the journal file. If there are ** not enough bytes left in the journal file for a complete header, or ** it is corrupted, then a process must of failed while writing it. ** This indicates nothing more needs to be rolled back. */ rc = readJournalHdr(pPager, szJ, &nRec, &mxPg); if( rc!=SQLITE_OK ){ if( rc==SQLITE_DONE ){ rc = SQLITE_OK; } goto end_playback; } /* If nRec is 0xffffffff, then this journal was created by a process ** working in no-sync mode. This means that the rest of the journal ** file consists of pages, there are no more journal headers. Compute ** the value of nRec based on this assumption. */ if( nRec==0xffffffff ){ assert( pPager->journalOff==JOURNAL_HDR_SZ(pPager) ); nRec = (szJ - JOURNAL_HDR_SZ(pPager))/JOURNAL_PG_SZ(pPager); } /* If this is the first header read from the journal, truncate the ** database file back to it's original size. */ if( pPager->state>=PAGER_EXCLUSIVE && pPager->journalOff==JOURNAL_HDR_SZ(pPager) ){ assert( pPager->origDbSize==0 || pPager->origDbSize==mxPg ); rc = pager_truncate(pPager, mxPg); if( rc!=SQLITE_OK ){ goto end_playback; } pPager->dbSize = mxPg; } /* rc = sqlite3OsSeek(&pPager->jfd, JOURNAL_HDR_SZ(pPager)); */ if( rc!=SQLITE_OK ) goto end_playback; /* Copy original pages out of the journal and back into the database file. */ for(i=0; i<nRec; i++){ rc = pager_playback_one_page(pPager, &pPager->jfd, 1); if( rc!=SQLITE_OK ){ if( rc==SQLITE_DONE ){ rc = SQLITE_OK; pPager->journalOff = szJ; break; }else{ goto end_playback; } } } } /* Pages that have been written to the journal but never synced ** where not restored by the loop above. We have to restore those ** pages by reading them back from the original database. */ assert( rc==SQLITE_OK ); pager_reload_cache(pPager);end_playback: if( rc==SQLITE_OK ){ rc = pager_unwritelock(pPager); } if( zMaster ){ /* If there was a master journal and this routine will return true, ** see if it is possible to delete the master journal. */ if( rc==SQLITE_OK ){ rc = pager_delmaster(zMaster); } sqliteFree(zMaster); } /* The Pager.sectorSize variable may have been updated while rolling ** back a journal created by a process with a different PAGER_SECTOR_SIZE ** value. Reset it to the correct value for this process. */ pPager->sectorSize = PAGER_SECTOR_SIZE; return rc;}/*** Playback the statement journal.**** This is similar to playing back the transaction journal but with** a few extra twists.**** (1) The number of pages in the database file at the start of** the statement is stored in pPager->stmtSize, not in the** journal file itself.**** (2) In addition to playing back the statement journal, also** playback all pages of the transaction journal beginning** at offset pPager->stmtJSize.*/static int pager_stmt_playback(Pager *pPager){ i64 szJ; /* Size of the full journal */ i64 hdrOff; int nRec; /* Number of Records */ int i; /* Loop counter */ int rc; szJ = pPager->journalOff;#ifndef NDEBUG { i64 os_szJ; rc = sqlite3OsFileSize(&pPager->jfd, &os_szJ); if( rc!=SQLITE_OK ) return rc; assert( szJ==os_szJ ); }#endif /* Set hdrOff to be the offset to the first journal header written ** this statement transaction, or the end of the file if no journal ** header was written. */ hdrOff = pPager->stmtHdrOff; assert( pPager->fullSync || !hdrOff ); if( !hdrOff ){ hdrOff = szJ; } /* Truncate the database back to its original size. */ if( pPager->state>=PAGER_EXCLUSIVE ){ rc = pager_truncate(pPager, pPager->stmtSize); } pPager->dbSize = pPager->stmtSize; /* Figure out how many records are in the statement journal. */ assert( pPager->stmtInUse && pPager->journalOpen ); sqlite3OsSeek(&pPager->stfd, 0); nRec = pPager->stmtNRec; /* Copy original pages out of the statement journal and back into the ** database file. Note that the statement journal omits checksums from ** each record since power-failure recovery is not important to statement ** journals. */ for(i=nRec-1; i>=0; i--){ rc = pager_playback_one_page(pPager, &pPager->stfd, 0); assert( rc!=SQLITE_DONE ); if( rc!=SQLITE_OK ) goto end_stmt_playback; } /* Now roll some pages back from the transaction journal. Pager.stmtJSize ** was the size of the journal file when this statement was started, so ** everything after that needs to be rolled back, either into the ** database, the memory cache, or both. ** ** If it is not zero, then Pager.stmtHdrOff is the offset to the start ** of the first journal header written during this statement transaction. */ rc = sqlite3OsSeek(&pPager->jfd, pPager->stmtJSize); if( rc!=SQLITE_OK ){ goto end_stmt_playback; } pPager->journalOff = pPager->stmtJSize; pPager->cksumInit = pPager->stmtCksum; assert( JOURNAL_HDR_SZ(pPager)<(pPager->pageSize+8) ); while( pPager->journalOff <= (hdrOff-(pPager->pageSize+8)) ){ rc = pager_playback_one_page(pPager, &pPager->jfd, 1); assert( rc!=SQLITE_DONE ); if( rc!=SQLITE_OK ) goto end_stmt_playback; } while( pPager->journalOff < szJ ){ u32 nRec; u32 dummy; rc = readJournalHdr(pPager, szJ, &nRec, &dummy); if( rc!=SQLITE_OK ){ assert( rc!=SQLITE_DONE ); goto end_stmt_playback; } if( nRec==0 ){ nRec = (szJ - pPager->journalOff) / (pPager->pageSize+8); } for(i=nRec-1; i>=0 && pPager->journalOff < szJ; i--){ rc = pager_playback_one_page(pPager, &pPager->jfd, 1); assert( rc!=SQLITE_DONE ); if( rc!=SQLITE_OK ) goto end_stmt_playback; } } pPager->journalOff = szJ; end_stmt_playback: if( rc!=SQLITE_OK ){ pPager->errMask |= PAGER_ERR_CORRUPT; rc = SQLITE_CORRUPT; /* bkpt-CORRUPT */ }else{ pPager->journalOff = szJ; /* pager_reload_cache(pPager); */ } return rc;}/*** Change the maximum number of in-memory pages that are allowed.*/void sqlite3pager_set_cachesize(Pager *pPager, int mxPage){ if( mxPage>10 ){ pPager->mxPage = mxPage; }else{ pPager->mxPage = 10; }}/*** 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:**** OFF sqlite3OsSync() is never called. This is the default** for temporary and transient files.**** NORMAL The journal is synced once before writes begin on the** database. This is normally adequate protection, but** it is theoretically possible, though very unlikely,** that an inopertune power failure could leave the journal** in a state which would cause damage to the database** when it is rolled back.**** FULL The journal is synced twice before writes begin on the** database (with some additional information - the nRec field** of the journal header - being written in between the two** syncs). If we assume that writing a** single disk sector is atomic, then this mode provides** assurance that the journal will not be corrupted to the** point of causing damage to the database during rollback.**** Numeric values associated with these states are OFF==1, NORMAL=2,** and FULL=3.*/#ifndef SQLITE_OMIT_PAGER_PRAGMASvoid sqlite3pager_set_safety_level(Pager *pPager, int level){ pPager->noSync = level==1 || pPager->tempFile; pPager->fullSync = level==3 && !pPager->tempFile;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -