?? pager.c
字號:
** 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 ){ sqliteRandomness(sizeof(pPager->cksumInit), &pPager->cksumInit); 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; int rc = SQLITE_OK; assert( pPg->nRef>0 ); assert( pPager->state!=SQLITE_UNLOCK ); if( pPager->state==SQLITE_READLOCK ){ assert( pPager->aInJournal==0 ); rc = sqliteOsWriteLock(&pPager->fd); if( rc!=SQLITE_OK ){ return rc; } pPager->state = SQLITE_WRITELOCK; pPager->dirtyFile = 0; TRACE1("TRANSACTION\n"); if( pPager->useJournal && !pPager->tempFile ){ rc = pager_open_journal(pPager); } } return rc;}/*** Mark a data page as writeable. The page is written into the journal ** if it is not there already. This routine must be called before making** changes to a page.**** The first time this routine is called, the pager creates a new** journal and acquires a write lock on the database. If the write** lock could not be acquired, this routine returns SQLITE_BUSY. The** calling routine must check for that return value and be careful not to** change any page data until this routine returns SQLITE_OK.**** If the journal file could not be written because the disk is full,** then this routine returns SQLITE_FULL and does an immediate rollback.** All subsequent write attempts also return SQLITE_FULL until there** is a call to sqlitepager_commit() or sqlitepager_rollback() to** reset.*/int sqlitepager_write(void *pData){ PgHdr *pPg = DATA_TO_PGHDR(pData); Pager *pPager = pPg->pPager; int rc = SQLITE_OK; /* Check for errors */ if( pPager->errMask ){ return pager_errcode(pPager); } if( pPager->readOnly ){ return SQLITE_PERM; } /* Mark the page as dirty. If the page has already been written ** to the journal then we can return right away. */ pPg->dirty = 1; if( pPg->inJournal && (pPg->inCkpt || pPager->ckptInUse==0) ){ pPager->dirtyFile = 1; return SQLITE_OK; } /* If we get this far, it means that the page needs to be ** written to the transaction journal or the ckeckpoint journal ** or both. ** ** First check to see that the transaction journal exists and ** create it if it does not. */ assert( pPager->state!=SQLITE_UNLOCK ); rc = sqlitepager_begin(pData); if( rc!=SQLITE_OK ){ return rc; } assert( pPager->state==SQLITE_WRITELOCK ); if( !pPager->journalOpen && pPager->useJournal ){ rc = pager_open_journal(pPager); if( rc!=SQLITE_OK ) return rc; } assert( pPager->journalOpen || !pPager->useJournal ); pPager->dirtyFile = 1; /* The transaction journal now exists and we have a write lock on the ** main database file. Write the current page to the transaction ** journal if it is not there already. */ if( !pPg->inJournal && pPager->useJournal ){ if( (int)pPg->pgno <= pPager->origDbSize ){ int szPg; u32 saved; if( journal_format>=JOURNAL_FORMAT_3 ){ u32 cksum = pager_cksum(pPager, pPg->pgno, pData); saved = *(u32*)PGHDR_TO_EXTRA(pPg); store32bits(cksum, pPg, SQLITE_PAGE_SIZE); szPg = SQLITE_PAGE_SIZE+8; }else{ szPg = SQLITE_PAGE_SIZE+4; } store32bits(pPg->pgno, pPg, -4); CODEC(pPager, pData, pPg->pgno, 7); rc = sqliteOsWrite(&pPager->jfd, &((char*)pData)[-4], szPg); TRACE3("JOURNAL %d %d\n", pPg->pgno, pPg->needSync); CODEC(pPager, pData, pPg->pgno, 0); if( journal_format>=JOURNAL_FORMAT_3 ){ *(u32*)PGHDR_TO_EXTRA(pPg) = saved; } if( rc!=SQLITE_OK ){ sqlitepager_rollback(pPager); pPager->errMask |= PAGER_ERR_FULL; return rc; } pPager->nRec++; assert( pPager->aInJournal!=0 ); pPager->aInJournal[pPg->pgno/8] |= 1<<(pPg->pgno&7); pPg->needSync = !pPager->noSync; pPg->inJournal = 1; if( pPager->ckptInUse ){ pPager->aInCkpt[pPg->pgno/8] |= 1<<(pPg->pgno&7); page_add_to_ckpt_list(pPg); } }else{ pPg->needSync = !pPager->journalStarted && !pPager->noSync; TRACE3("APPEND %d %d\n", pPg->pgno, pPg->needSync); } if( pPg->needSync ){ pPager->needSync = 1; } } /* If the checkpoint journal is open and the page is not in it, ** then write the current page to the checkpoint journal. Note that ** the checkpoint journal always uses the simplier format 2 that lacks ** checksums. The header is also omitted from the checkpoint journal. */ if( pPager->ckptInUse && !pPg->inCkpt && (int)pPg->pgno<=pPager->ckptSize ){ assert( pPg->inJournal || (int)pPg->pgno>pPager->origDbSize ); store32bits(pPg->pgno, pPg, -4); CODEC(pPager, pData, pPg->pgno, 7); rc = sqliteOsWrite(&pPager->cpfd, &((char*)pData)[-4], SQLITE_PAGE_SIZE+4); TRACE2("CKPT-JOURNAL %d\n", pPg->pgno); CODEC(pPager, pData, pPg->pgno, 0); if( rc!=SQLITE_OK ){ sqlitepager_rollback(pPager); pPager->errMask |= PAGER_ERR_FULL; return rc; } pPager->ckptNRec++; assert( pPager->aInCkpt!=0 ); pPager->aInCkpt[pPg->pgno/8] |= 1<<(pPg->pgno&7); page_add_to_ckpt_list(pPg); } /* Update the database size and return. */ if( pPager->dbSize<(int)pPg->pgno ){ pPager->dbSize = pPg->pgno; } return rc;}/*** Return TRUE if the page given in the argument was previously passed** to sqlitepager_write(). In other words, return TRUE if it is ok** to change the content of the page.*/int sqlitepager_iswriteable(void *pData){ PgHdr *pPg = DATA_TO_PGHDR(pData); return pPg->dirty;}/*** Replace the content of a single page with the information in the third** argument.*/int sqlitepager_overwrite(Pager *pPager, Pgno pgno, void *pData){ void *pPage; int rc; rc = sqlitepager_get(pPager, pgno, &pPage); if( rc==SQLITE_OK ){ rc = sqlitepager_write(pPage); if( rc==SQLITE_OK ){ memcpy(pPage, pData, SQLITE_PAGE_SIZE); } sqlitepager_unref(pPage); } return rc;}/*** A call to this routine tells the pager that it is not necessary to** write the information on page "pgno" back to the disk, even though** that page might be marked as dirty.**** The overlying software layer calls this routine when all of the data** on the given page is unused. The pager marks the page as clean so** that it does not get written to disk.**** Tests show that this optimization, together with the** sqlitepager_dont_rollback() below, more than double the speed** of large INSERT operations and quadruple the speed of large DELETEs.**** When this routine is called, set the alwaysRollback flag to true.** Subsequent calls to sqlitepager_dont_rollback() for the same page** will thereafter be ignored. This is necessary to avoid a problem** where a page with data is added to the freelist during one part of** a transaction then removed from the freelist during a later part** of the same transaction and reused for some other purpose. When it** is first added to the freelist, this routine is called. When reused,** the dont_rollback() routine is called. But because the page contains** critical data, we still need to be sure it gets rolled back in spite** of the dont_rollback() call.*/void sqlitepager_dont_write(Pager *pPager, Pgno pgno){ PgHdr *pPg; pPg = pager_lookup(pPager, pgno); pPg->alwaysRollback = 1; if( pPg && pPg->dirty ){ if( pPager->d
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -