pager.c
来自「SQLite 2.8.6 源代码,用来在Linux/Unix/Windows上编」· C语言 代码 · 共 2,008 行 · 第 1/5 页
C
2,008 行
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); rc = sqliteOsWrite(&pPager->jfd, &((char*)pData)[-4], szPg); 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); } TRACE3("JOURNAL %d %d\n", pPg->pgno, pPg->needSync); }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); rc = sqliteOsWrite(&pPager->cpfd, &((char*)pData)[-4], SQLITE_PAGE_SIZE+4); 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->dbSize==(int)pPg->pgno && pPager->origDbSize<pPager->dbSize ){ /* If this pages is the last page in the file and the file has grown ** during the current transaction, then do NOT mark the page as clean. ** When the database file grows, we must make sure that the last page ** gets written at least once so that the disk file will be the correct ** size. If you do not write this page and the size of the file ** on the disk ends up being too small, that can lead to database ** corruption during the next transaction. */ }else{ TRACE2("DONT_WRITE %d\n", pgno); pPg->dirty = 0; } }}/*** A call to this routine tells the pager that if a rollback occurs,** it is not necessary to restore the data on the given page. This** means that the pager does not have to record the given page in the** rollback journal.*/void sqlitepager_dont_rollback(void *pData){ PgHdr *pPg = DATA_TO_PGHDR(pData); Pager *pPager = pPg->pPager; if( pPager->state!=SQLITE_WRITELOCK || pPager->journalOpen==0 ) return; if( pPg->alwaysRollback || pPager->alwaysRollback ) return; if( !pPg->inJournal && (int)pPg->pgno <= pPager->origDbSize ){ assert( pPager->aInJournal!=0 ); pPager->aInJournal[pPg->pgno/8] |= 1<<(pPg->pgno&7); pPg->inJournal = 1; if( pPager->ckptInUse ){ pPager->aInCkpt[pPg->pgno/8] |= 1<<(pPg->pgno&7); page_add_to_ckpt_list(pPg); } TRACE2("DONT_ROLLBACK %d\n", pPg->pgno); } if( pPager->ckptInUse && !pPg->inCkpt && (int)pPg->pgno<=pPager->ckptSize ){ assert( pPg->inJournal || (int)pPg->pgno>pPager->origDbSize ); assert( pPager->aInCkpt!=0 ); pPager->aInCkpt[pPg->pgno/8] |= 1<<(pPg->pgno&7); page_add_to_ckpt_list(pPg); }}/*** Commit all changes to the database and release the write lock.**** If the commit fails for any reason, a rollback attempt is made** and an error code is returned. If the commit worked, SQLITE_OK** is returned.*/int sqlitepager_commit(Pager *pPager){ int rc; PgHdr *pPg; if( pPager->errMask==PAGER_ERR_FULL ){ rc = sqlitepager_rollback(pPager); if( rc==SQLITE_OK ){ rc = SQLITE_FULL; } return rc; } if( pPager->errMask!=0 ){ rc = pager_errcode(pPager); return rc; } if( pPager->state!=SQLITE_WRITELOCK ){ return SQLITE_ERROR; } TRACE1("COMMIT\n"); if( pPager->dirtyFile==0 ){ /* Exit early (without doing the time-consuming sqliteOsSync() calls) ** if there have been no changes to the database file. */ assert( pPager->needSync==0 ); rc = pager_unwritelock(pPager); pPager->dbSize = -1; return rc; } assert( pPager->journalOpen ); if( pPager->needSync && sqliteOsSync(&pPager->jfd)!=SQLITE_OK ){ goto commit_abort; } pPg = pager_get_all_dirty_pages(pPager); if( pPg ){ rc = pager_write_pagelist(pPg); if( rc || (!pPager->noSync && sqliteOsSync(&pPager->fd)!=SQLITE_OK) ){ goto commit_abort; } } rc = pager_unwritelock(pPager); pPager->dbSize = -1; return rc; /* Jump here if anything goes wrong during the commit process. */commit_abort: rc = sqlitepager_rollback(pPager); if( rc==SQLITE_OK ){ rc = SQLITE_FULL; } return rc;}/*** Rollback all changes. The database falls back to read-only mode.** All in-memory cache pages revert to their original data contents.** The journal is deleted.**** This routine cannot fail unless some other process is not following** the correct locking protocol (SQLITE_PROTOCOL) or unless some other** process is writing trash into the journal file (SQLITE_CORRUPT) or** unless a prior malloc() failed (SQLITE_NOMEM). Appropriate error** codes are returned for all these occasions. Otherwise,** SQLITE_OK is returned.*/int sqlitepager_rollback(Pager *pPager){ int rc; TRACE1("ROLLBACK\n"); if( !pPager->dirtyFile || !pPager->journalOpen ){ rc = pager_unwritelock(pPager); pPager->dbSize = -1; return rc; } if( pPager->errMask!=0 && pPager->errMask!=PAGER_ERR_FULL ){ if( pPager->state>=SQLITE_WRITELOCK ){ pager_playback(pPager, 1); } return pager_errcode(pPager); } if( pPager->state!=SQLITE_WRITELOCK ){ return SQLITE_OK; } rc = pager_playback(pPager, 1); if( rc!=SQLITE_OK ){ rc = SQLITE_CORRUPT; pPager->errMask |= PAGER_ERR_CORRUPT; } pPager->dbSize = -1; return rc;}/*** Return TRUE if the database file is opened read-only. Return FALSE** if the database is (in theory) writable.*/int sqlitepager_isreadonly(Pager *pPager){ return pPager->readOnly;}/*** This routine is used for testing and analysis only.*/int *sqlitepager_stats(Pager *pPager){ static int a[9]; a[0] = pPager->nRef; a[1] = pPager->nPage; a[2] = pPager->mxPage; a[3] = pPager->dbSize; a[4] = pPager->state; a[5] = pPager->errMask; a[6] = pPager->nHit; a[7] = pPager->nMiss; a[8] = pPager->nOvfl; return a;}/*** Set the checkpoint.**** This routine should be called with the transaction journal already** open. A new checkpoint journal is created that can be used to rollback** changes of a single SQL command within a larger transaction.*/int sqlitepager_ckpt_begin(Pager *pPager){ int rc; char zTemp[SQLITE_TEMPNAME_SIZE]; if( !pPager->journalOpen ){ pPager->ckptAutoopen = 1; return SQLITE_OK; } assert( pPager->journalOpen ); assert( !pPager->ckptInUse ); pPager->aInCkpt = sqliteMalloc( pPager->dbSize/8 + 1 ); if( pPager->aInCkpt==0 ){ sqliteOsReadLock(&pPager->fd); return
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?