pager.c
来自「SQLite 2.8.6 源代码,用来在Linux/Unix/Windows上编」· C语言 代码 · 共 2,008 行 · 第 1/5 页
C
2,008 行
int rc; /* Make sure we have not hit any critical errors. */ assert( pPager!=0 ); assert( pgno!=0 ); *ppPage = 0; if( pPager->errMask & ~(PAGER_ERR_FULL) ){ return pager_errcode(pPager); } /* If this is the first page accessed, then get a read lock ** on the database file. */ if( pPager->nRef==0 ){ rc = sqliteOsReadLock(&pPager->fd); if( rc!=SQLITE_OK ){ return rc; } pPager->state = SQLITE_READLOCK; /* If a journal file exists, try to play it back. */ if( pPager->useJournal && sqliteOsFileExists(pPager->zJournal) ){ int rc; /* Get a write lock on the database */ rc = sqliteOsWriteLock(&pPager->fd); if( rc!=SQLITE_OK ){ if( sqliteOsUnlock(&pPager->fd)!=SQLITE_OK ){ /* This should never happen! */ rc = SQLITE_INTERNAL; } return rc; } pPager->state = SQLITE_WRITELOCK; /* Open the journal for reading only. Return SQLITE_BUSY if ** we are unable to open the journal file. ** ** The journal file does not need to be locked itself. The ** journal file is never open unless the main database file holds ** a write lock, so there is never any chance of two or more ** processes opening the journal at the same time. */ rc = sqliteOsOpenReadOnly(pPager->zJournal, &pPager->jfd); if( rc!=SQLITE_OK ){ rc = sqliteOsUnlock(&pPager->fd); assert( rc==SQLITE_OK ); return SQLITE_BUSY; } pPager->journalOpen = 1; pPager->journalStarted = 0; /* Playback and delete the journal. Drop the database write ** lock and reacquire the read lock. */ rc = pager_playback(pPager, 0); if( rc!=SQLITE_OK ){ return rc; } } pPg = 0; }else{ /* Search for page in cache */ pPg = pager_lookup(pPager, pgno); } if( pPg==0 ){ /* The requested page is not in the page cache. */ int h; pPager->nMiss++; if( pPager->nPage<pPager->mxPage || pPager->pFirst==0 ){ /* Create a new page */ pPg = sqliteMallocRaw( sizeof(*pPg) + SQLITE_PAGE_SIZE + sizeof(u32) + pPager->nExtra ); if( pPg==0 ){ pager_unwritelock(pPager); pPager->errMask |= PAGER_ERR_MEM; return SQLITE_NOMEM; } memset(pPg, 0, sizeof(*pPg)); pPg->pPager = pPager; pPg->pNextAll = pPager->pAll; if( pPager->pAll ){ pPager->pAll->pPrevAll = pPg; } pPg->pPrevAll = 0; pPager->pAll = pPg; pPager->nPage++; }else{ /* Find a page to recycle. Try to locate a page that does not ** require us to do an fsync() on the journal. */ pPg = pPager->pFirstSynced; /* If we could not find a page that does not require an fsync() ** on the journal file then fsync the journal file. This is a ** very slow operation, so we work hard to avoid it. But sometimes ** it can't be helped. */ if( pPg==0 ){ int rc = syncAllPages(pPager); if( rc!=0 ){ sqlitepager_rollback(pPager); return SQLITE_IOERR; } pPg = pPager->pFirst; } assert( pPg->nRef==0 ); /* Write the page to the database file if it is dirty. */ if( pPg->dirty ){ assert( pPg->needSync==0 ); pPg->pDirty = 0; rc = pager_write_pagelist( pPg ); if( rc!=SQLITE_OK ){ sqlitepager_rollback(pPager); return SQLITE_IOERR; } } assert( pPg->dirty==0 ); /* If the page we are recycling is marked as alwaysRollback, then ** set the global alwaysRollback flag, thus disabling the ** sqlite_dont_rollback() optimization for the rest of this transaction. ** It is necessary to do this because the page marked alwaysRollback ** might be reloaded at a later time but at that point we won't remember ** that is was marked alwaysRollback. This means that all pages must ** be marked as alwaysRollback from here on out. */ if( pPg->alwaysRollback ){ pPager->alwaysRollback = 1; } /* Unlink the old page from the free list and the hash table */ if( pPg==pPager->pFirstSynced ){ PgHdr *p = pPg->pNextFree; while( p && p->needSync ){ p = p->pNextFree; } pPager->pFirstSynced = p; } if( pPg->pPrevFree ){ pPg->pPrevFree->pNextFree = pPg->pNextFree; }else{ assert( pPager->pFirst==pPg ); pPager->pFirst = pPg->pNextFree; } if( pPg->pNextFree ){ pPg->pNextFree->pPrevFree = pPg->pPrevFree; }else{ assert( pPager->pLast==pPg ); pPager->pLast = pPg->pPrevFree; } pPg->pNextFree = pPg->pPrevFree = 0; if( pPg->pNextHash ){ pPg->pNextHash->pPrevHash = pPg->pPrevHash; } if( pPg->pPrevHash ){ pPg->pPrevHash->pNextHash = pPg->pNextHash; }else{ h = pager_hash(pPg->pgno); assert( pPager->aHash[h]==pPg ); pPager->aHash[h] = pPg->pNextHash; } pPg->pNextHash = pPg->pPrevHash = 0; pPager->nOvfl++; } pPg->pgno = pgno; if( pPager->aInJournal && (int)pgno<=pPager->origDbSize ){ sqliteCheckMemory(pPager->aInJournal, pgno/8); assert( pPager->journalOpen ); pPg->inJournal = (pPager->aInJournal[pgno/8] & (1<<(pgno&7)))!=0; pPg->needSync = 0; }else{ pPg->inJournal = 0; pPg->needSync = 0; } if( pPager->aInCkpt && (int)pgno<=pPager->ckptSize && (pPager->aInCkpt[pgno/8] & (1<<(pgno&7)))!=0 ){ page_add_to_ckpt_list(pPg); }else{ page_remove_from_ckpt_list(pPg); } pPg->dirty = 0; pPg->nRef = 1; REFINFO(pPg); pPager->nRef++; h = pager_hash(pgno); pPg->pNextHash = pPager->aHash[h]; pPager->aHash[h] = pPg; if( pPg->pNextHash ){ assert( pPg->pNextHash->pPrevHash==0 ); pPg->pNextHash->pPrevHash = pPg; } if( pPager->nExtra>0 ){ memset(PGHDR_TO_EXTRA(pPg), 0, pPager->nExtra); } if( pPager->dbSize<0 ) sqlitepager_pagecount(pPager); if( pPager->errMask!=0 ){ sqlitepager_unref(PGHDR_TO_DATA(pPg)); rc = pager_errcode(pPager); return rc; } if( pPager->dbSize<(int)pgno ){ memset(PGHDR_TO_DATA(pPg), 0, SQLITE_PAGE_SIZE); }else{ int rc; sqliteOsSeek(&pPager->fd, (pgno-1)*(off_t)SQLITE_PAGE_SIZE); rc = sqliteOsRead(&pPager->fd, PGHDR_TO_DATA(pPg), SQLITE_PAGE_SIZE); if( rc!=SQLITE_OK ){ off_t fileSize; if( sqliteOsFileSize(&pPager->fd,&fileSize)!=SQLITE_OK || fileSize>=pgno*SQLITE_PAGE_SIZE ){ sqlitepager_unref(PGHDR_TO_DATA(pPg)); return rc; }else{ memset(PGHDR_TO_DATA(pPg), 0, SQLITE_PAGE_SIZE); } } } }else{ /* The requested page is in the page cache. */ pPager->nHit++; page_ref(pPg); } *ppPage = PGHDR_TO_DATA(pPg); return SQLITE_OK;}/*** Acquire a page if it is already in the in-memory cache. Do** not read the page from disk. Return a pointer to the page,** or 0 if the page is not in cache.**** See also sqlitepager_get(). The difference between this routine** and sqlitepager_get() is that _get() will go to the disk and read** in the page if the page is not already in cache. This routine** returns NULL if the page is not in cache or if a disk I/O error ** has ever happened.*/void *sqlitepager_lookup(Pager *pPager, Pgno pgno){ PgHdr *pPg; assert( pPager!=0 ); assert( pgno!=0 ); if( pPager->errMask & ~(PAGER_ERR_FULL) ){ return 0; } /* if( pPager->nRef==0 ){ ** return 0; ** } */ pPg = pager_lookup(pPager, pgno); if( pPg==0 ) return 0; page_ref(pPg); return PGHDR_TO_DATA(pPg);}/*** Release a page.**** If the number of references to the page drop to zero, then the** page is added to the LRU list. When all references to all pages** are released, a rollback occurs and the lock on the database is** removed.*/int sqlitepager_unref(void *pData){ PgHdr *pPg; /* Decrement the reference count for this page */ pPg = DATA_TO_PGHDR(pData); assert( pPg->nRef>0 ); pPg->nRef--; REFINFO(pPg); /* When the number of references to a page reach 0, call the ** destructor and add the page to the freelist. */ if( pPg->nRef==0 ){ Pager *pPager; pPager = pPg->pPager; pPg->pNextFree = 0; pPg->pPrevFree = pPager->pLast; pPager->pLast = pPg; if( pPg->pPrevFree ){ pPg->pPrevFree->pNextFree = pPg; }else{ pPager->pFirst = pPg; } if( pPg->needSync==0 && pPager->pFirstSynced==0 ){ pPager->pFirstSynced = pPg; } if( pPager->xDestructor ){ pPager->xDestructor(pData); } /* When all pages reach the freelist, drop the read lock from ** the database file. */ pPager->nRef--; assert( pPager->nRef>=0 ); if( pPager->nRef==0 ){ pager_reset(pPager); } } return SQLITE_OK;}/*** Create a journal file for pPager. There should already be a write** lock on the database file when this routine is called.**** Return SQLITE_OK if everything. Return an error code and release the** write lock if anything goes wrong.*/static int pager_open_journal(Pager *pPager){ int rc; assert( pPager->state==SQLITE_WRITELOCK ); assert( pPager->journalOpen==0 ); assert( pPager->useJournal ); sqlitepager_pagecount(pPager); pPager->aInJournal = sqliteMalloc( pPager->dbSize/8 + 1 ); if( pPager->aInJournal==0 ){ sqliteOsReadLock(&pPager->fd); pPager->state = SQLITE_READLOCK; return SQLITE_NOMEM; } rc = sqliteOsOpenExclusive(pPager->zJournal, &pPager->jfd,pPager->tempFile); if( rc!=SQLITE_OK ){ sqliteFree(pPager->aInJournal); pPager->aInJournal = 0; sqliteOsReadLock(&pPager->fd); pPager->state = SQLITE_READLOCK; return SQLITE_CANTOPEN; } sqliteOsOpenDirectory(pPager->zDirectory, &pPager->jfd); pPager->journalOpen = 1; pPager->journalStarted = 0; pPager->needSync = 0; pPager->alwaysRollback = 0; pPager->nRec = 0; if( pPager->errMask!=0 ){ rc = pager_errcode(pPager); return rc; } pPager->origDbSize = pPager->dbSize; if( journal_format==JOURNAL_FORMAT_3 ){ rc = sqliteOsWrite(&pPager->jfd, aJournalMagic3, sizeof(aJournalMagic3)); if( rc==SQLITE_OK ){ rc = write32bits(&pPager->jfd, pPager->noSync ? 0xffffffff : 0); } if( rc==SQLITE_OK ){ pPager->cksumInit = (u32)sqliteRandomInteger(); rc = write32bits(&pPager->jfd, pPager->cksumInit); } }else if( journal_format==JOURNAL_FORMAT_2 ){ rc = sqliteOsWrite(&pPager->jfd, aJournalMagic2, sizeof(aJournalMagic2)); }else{ assert( journal_format==JOURNAL_FORMAT_1 ); rc = sqliteOsWrite(&pPager->jfd, aJournalMagic1, sizeof(aJournalMagic1)); } if( rc==SQLITE_OK ){ rc = write32bits(&pPager->jfd, pPager->dbSize); } if( pPager->ckptAutoopen && rc==SQLITE_OK ){ rc = sqlitepager_ckpt_begin(pPager); } if( rc!=SQLITE_OK ){ rc = pager_unwritelock(pPager); if( rc==SQLITE_OK ){ rc = SQLITE_FULL; } } return rc; }/*** Acquire a write-lock on the database. The lock is removed when** the any of the following happen:**** * sqlitepager_commit() is called.** * sqlitepager_rollback() is called.** * sqlitepager_close() is called.** * sqlitepager_unref() is called to on every outstanding page.**** The parameter to this routine is a pointer to any open page of the** database file. Nothing changes about the page - it is used merely** to acquire a pointer to the Pager structure and as proof that there** is already a read-lock on the database.**** A journal file is opened if this is not a temporary file. For** temporary files, the opening of the journal file is deferred until** there is an actual need to write to the journal.**** If the database is already write-locked, this routine is a no-op.*/int sqlitepager_begin(void *pData){ PgHdr *pPg = DATA_TO_PGHDR(pData); Pager *pPager = pPg->pPager;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?