📄 pager.c
字号:
u8 setMaster; /* True if a m-j name has been written to jrnl */ u8 doNotSync; /* Boolean. While true, do not spill the cache */ u8 exclusiveMode; /* Boolean. True if locking_mode==EXCLUSIVE */ u8 changeCountDone; /* Set after incrementing the change-counter */ u32 vfsFlags; /* Flags for sqlite3_vfs.xOpen() */ int errCode; /* One of several kinds of errors */ int dbSize; /* Number of pages in the file */ int origDbSize; /* dbSize before the current change */ int stmtSize; /* Size of database (in pages) at stmt_begin() */ int nRec; /* Number of pages written to the journal */ u32 cksumInit; /* Quasi-random value added to every checksum */ int stmtNRec; /* Number of records in stmt subjournal */ int nExtra; /* Add this many bytes to each in-memory page */ int pageSize; /* Number of bytes in a page */ int nPage; /* Total number of in-memory pages */ int nRef; /* Number of in-memory pages with PgHdr.nRef>0 */ int mxPage; /* Maximum number of pages to hold in cache */ Pgno mxPgno; /* Maximum allowed size of the database */ u8 *aInJournal; /* One bit for each page in the database file */ u8 *aInStmt; /* One bit for each page in the database */ char *zFilename; /* Name of the database file */ char *zJournal; /* Name of the journal file */ char *zDirectory; /* Directory hold database and journal files */ char *zStmtJrnl; /* Name of the statement journal file */ sqlite3_file *fd, *jfd; /* File descriptors for database and journal */ sqlite3_file *stfd; /* File descriptor for the statement subjournal*/ BusyHandler *pBusyHandler; /* Pointer to sqlite.busyHandler */ PagerLruList lru; /* LRU list of free pages */ PgHdr *pAll; /* List of all pages */ PgHdr *pStmt; /* List of pages in the statement subjournal */ PgHdr *pDirty; /* List of all dirty pages */ i64 journalOff; /* Current byte offset in the journal file */ i64 journalHdr; /* Byte offset to previous journal header */ i64 stmtHdrOff; /* First journal header written this statement */ i64 stmtCksum; /* cksumInit when statement was started */ i64 stmtJSize; /* Size of journal at stmt_begin() */ int sectorSize; /* Assumed sector size during rollback */#ifdef SQLITE_TEST int nHit, nMiss; /* Cache hits and missing */ int nRead, nWrite; /* Database pages read/written */#endif void (*xDestructor)(DbPage*,int); /* Call this routine when freeing pages */ void (*xReiniter)(DbPage*,int); /* Call this routine when reloading pages */#ifdef SQLITE_HAS_CODEC void *(*xCodec)(void*,void*,Pgno,int); /* Routine for en/decoding data */ void *pCodecArg; /* First argument to xCodec() */#endif int nHash; /* Size of the pager hash table */ PgHdr **aHash; /* Hash table to map page number to PgHdr */#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT Pager *pNext; /* Doubly linked list of pagers on which */ Pager *pPrev; /* sqlite3_release_memory() will work */ int iInUseMM; /* Non-zero if unavailable to MM */ int iInUseDB; /* Non-zero if in sqlite3_release_memory() */#endif char *pTmpSpace; /* Pager.pageSize bytes of space for tmp use */ char dbFileVers[16]; /* Changes whenever database file changes */};/*** 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.*/#ifdef SQLITE_TESTint sqlite3_pager_readdb_count = 0; /* Number of full pages read from DB */int sqlite3_pager_writedb_count = 0; /* Number of full pages written to DB */int sqlite3_pager_writej_count = 0; /* Number of pages written to journal */int sqlite3_pager_pgfree_count = 0; /* Number of cache pages freed */# define PAGER_INCR(v) v++#else# define PAGER_INCR(v)#endif/*** The following variable points to the head of a double-linked list** of all pagers that are eligible for page stealing by the** sqlite3_release_memory() interface. Access to this list is** protected by the SQLITE_MUTEX_STATIC_MEM2 mutex.*/#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENTstatic Pager *sqlite3PagerList = 0;static PagerLruList sqlite3LruPageList = {0, 0, 0};#endif/*** Journal files begin with the following magic string. The data** was obtained from /dev/random. It is used only as a sanity check.**** Since version 2.8.0, the journal format contains additional sanity** checking information. If the power fails while the journal is begin** written, semi-random garbage data might appear in the journal** file after power is restored. If an attempt is then made** to roll the journal back, the database could be corrupted. The additional** sanity checking data is an attempt to discover the garbage in the** journal and ignore it.**** The sanity checking information for the new journal format consists** of a 32-bit checksum on each page of data. The checksum covers both** the page number and the pPager->pageSize bytes of data for the page.** This cksum is initialized to a 32-bit random value that appears in the** journal file right after the header. The random initializer is important,** because garbage data that appears at the end of a journal is likely** data that was once in other files that have now been deleted. If the** garbage data came from an obsolete journal file, the checksums might** be correct. But by initializing the checksum to random value which** is different for every journal, we minimize that risk.*/static const unsigned char aJournalMagic[] = { 0xd9, 0xd5, 0x05, 0xf9, 0x20, 0xa1, 0x63, 0xd7,};/*** The size of the header and of each page in the journal is determined** by the following macros.*/#define JOURNAL_PG_SZ(pPager) ((pPager->pageSize) + 8)/*** The journal header size for this pager. In the future, this could be** set to some value read from the disk controller. The important** characteristic is that it is the same size as a disk sector.*/#define JOURNAL_HDR_SZ(pPager) (pPager->sectorSize)/*** The macro MEMDB is true if we are dealing with an in-memory database.** We do this as a macro so that if the SQLITE_OMIT_MEMORYDB macro is set,** the value of MEMDB will be a constant and the compiler will optimize** out code that would never execute.*/#ifdef SQLITE_OMIT_MEMORYDB# define MEMDB 0#else# define MEMDB pPager->memDb#endif/*** Page number PAGER_MJ_PGNO is never used in an SQLite database (it is** reserved for working around a windows/posix incompatibility). It is** used in the journal to signify that the remainder of the journal file ** is devoted to storing a master journal name - there are no more pages to** roll back. See comments for function writeMasterJournal() for details.*//* #define PAGER_MJ_PGNO(x) (PENDING_BYTE/((x)->pageSize)) */#define PAGER_MJ_PGNO(x) ((PENDING_BYTE/((x)->pageSize))+1)/*** The maximum legal page number is (2^31 - 1).*/#define PAGER_MAX_PGNO 2147483647/*** The pagerEnter() and pagerLeave() routines acquire and release** a mutex on each pager. The mutex is recursive.**** This is a special-purpose mutex. It only provides mutual exclusion** between the Btree and the Memory Management sqlite3_release_memory()** function. It does not prevent, for example, two Btrees from accessing** the same pager at the same time. Other general-purpose mutexes in** the btree layer handle that chore.*/#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT static void pagerEnter(Pager *p){ p->iInUseDB++; if( p->iInUseMM && p->iInUseDB==1 ){ sqlite3_mutex *mutex; mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MEM2); p->iInUseDB = 0; sqlite3_mutex_enter(mutex); p->iInUseDB = 1; sqlite3_mutex_leave(mutex); } assert( p->iInUseMM==0 ); } static void pagerLeave(Pager *p){ p->iInUseDB--; assert( p->iInUseDB>=0 ); }#else# define pagerEnter(X)# define pagerLeave(X)#endif/*** Add page pPg to the end of the linked list managed by structure** pList (pPg becomes the last entry in the list - the most recently ** used). Argument pLink should point to either pPg->free or pPg->gfree,** depending on whether pPg is being added to the pager-specific or** global LRU list.*/static void listAdd(PagerLruList *pList, PagerLruLink *pLink, PgHdr *pPg){ pLink->pNext = 0; pLink->pPrev = pList->pLast;#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT assert(pLink==&pPg->free || pLink==&pPg->gfree); assert(pLink==&pPg->gfree || pList!=&sqlite3LruPageList);#endif if( pList->pLast ){ int iOff = (char *)pLink - (char *)pPg; PagerLruLink *pLastLink = (PagerLruLink *)(&((u8 *)pList->pLast)[iOff]); pLastLink->pNext = pPg; }else{ assert(!pList->pFirst); pList->pFirst = pPg; } pList->pLast = pPg; if( !pList->pFirstSynced && pPg->needSync==0 ){ pList->pFirstSynced = pPg; }}/*** Remove pPg from the list managed by the structure pointed to by pList.**** Argument pLink should point to either pPg->free or pPg->gfree, depending ** on whether pPg is being added to the pager-specific or global LRU list.*/static void listRemove(PagerLruList *pList, PagerLruLink *pLink, PgHdr *pPg){ int iOff = (char *)pLink - (char *)pPg;#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT assert(pLink==&pPg->free || pLink==&pPg->gfree); assert(pLink==&pPg->gfree || pList!=&sqlite3LruPageList);#endif if( pPg==pList->pFirst ){ pList->pFirst = pLink->pNext; } if( pPg==pList->pLast ){ pList->pLast = pLink->pPrev; } if( pLink->pPrev ){ PagerLruLink *pPrevLink = (PagerLruLink *)(&((u8 *)pLink->pPrev)[iOff]); pPrevLink->pNext = pLink->pNext; } if( pLink->pNext ){ PagerLruLink *pNextLink = (PagerLruLink *)(&((u8 *)pLink->pNext)[iOff]); pNextLink->pPrev = pLink->pPrev; } if( pPg==pList->pFirstSynced ){ PgHdr *p = pLink->pNext; while( p && p->needSync ){ PagerLruLink *pL = (PagerLruLink *)(&((u8 *)p)[iOff]); p = pL->pNext; } pList->pFirstSynced = p; } pLink->pNext = pLink->pPrev = 0;}/* ** Add page pPg to the list of free pages for the pager. If ** memory-management is enabled, also add the page to the global ** list of free pages.*/static void lruListAdd(PgHdr *pPg){ listAdd(&pPg->pPager->lru, &pPg->free, pPg);#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT if( !pPg->pPager->memDb ){ sqlite3_mutex_enter(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_LRU)); listAdd(&sqlite3LruPageList, &pPg->gfree, pPg); sqlite3_mutex_leave(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_LRU)); }#endif}/* ** Remove page pPg from the list of free pages for the associated pager.** If memory-management is enabled, also remove pPg from the global list** of free pages.*/static void lruListRemove(PgHdr *pPg){ listRemove(&pPg->pPager->lru, &pPg->free, pPg);#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT if( !pPg->pPager->memDb ){ sqlite3_mutex_enter(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_LRU)); listRemove(&sqlite3LruPageList, &pPg->gfree, pPg); sqlite3_mutex_leave(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_LRU)); }#endif}/* ** This function is called just after the needSync flag has been cleared** from all pages managed by pPager (usually because the journal file** has just been synced). It updates the pPager->lru.pFirstSynced variable** and, if memory-management is enabled, the sqlite3LruPageList.pFirstSynced** variable also.*/static void lruListSetFirstSynced(Pager *pPager){ pPager->lru.pFirstSynced = pPager->lru.pFirst;#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT if( !pPager->memDb ){ PgHdr *p; sqlite3_mutex_enter(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_LRU)); for(p=sqlite3LruPageList.pFirst; p && p->needSync; p=p->gfree.pNext); assert(p==pPager->lru.pFirstSynced || p==sqlite3LruPageList.pFirstSynced); sqlite3LruPageList.pFirstSynced = p; sqlite3_mutex_leave(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_LRU)); }#endif}/*** Return true if page *pPg has already been written to the statement** journal (or statement snapshot has been created, if *pPg is part** of an in-memory database).*/static int pageInStatement(PgHdr *pPg){ Pager *pPager = pPg->pPager; if( MEMDB ){ return PGHDR_TO_HIST(pPg, pPager)->inStmt; }else{ Pgno pgno = pPg->pgno; u8 *a = pPager->aInStmt; return (a && (int)pgno<=pPager->stmtSize && (a[pgno/8] & (1<<(pgno&7)))); }}/*** Change the size of the pager hash table to N. N must be a power** of two.*/static void pager_resize_hash_table(Pager *pPager, int N){ PgHdr **aHash, *pPg; assert( N>0 && (N&(N-1))==0 ); pagerLeave(pPager); sqlite3FaultBenign(SQLITE_FAULTINJECTOR_MALLOC, pPager->aHash!=0); aHash = sqlite3MallocZero( sizeof(aHash[0])*N ); sqlite3FaultBenign(SQLITE_FAULTINJECTOR_MALLOC, 0); pagerEnter(pPager); if( aHash==0 ){ /* Failure to rehash is not an error. It is only a performance hit. */ return; } sqlite3_free(pPager->aHash); pPager->nHash = N; pPager->aHash = aHash; for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){ int h; if( pPg->pgno==0 ){ assert( pPg->pNextHash==0 && pPg->pPrevHash==0 ); continue;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -