btree.c
来自「sqlite-3.4.1,嵌入式数据库.是一个功能强大的开源数据库,给学习和研发」· C语言 代码 · 共 2,088 行 · 第 1/5 页
C
2,088 行
for(i=0; i<nCell; i++){ u8 *pCell = findCell(pPage, i); rc = ptrmapPutOvflPtr(pPage, pCell); if( rc!=SQLITE_OK ){ goto set_child_ptrmaps_out; } if( !pPage->leaf ){ Pgno childPgno = get4byte(pCell); rc = ptrmapPut(pBt, childPgno, PTRMAP_BTREE, pgno); if( rc!=SQLITE_OK ) goto set_child_ptrmaps_out; } } if( !pPage->leaf ){ Pgno childPgno = get4byte(&pPage->aData[pPage->hdrOffset+8]); rc = ptrmapPut(pBt, childPgno, PTRMAP_BTREE, pgno); }set_child_ptrmaps_out: pPage->isInit = isInitOrig; return rc;}/*** Somewhere on pPage, which is guarenteed to be a btree page, not an overflow** page, is a pointer to page iFrom. Modify this pointer so that it points to** iTo. Parameter eType describes the type of pointer to be modified, as ** follows:**** PTRMAP_BTREE: pPage is a btree-page. The pointer points at a child ** page of pPage.**** PTRMAP_OVERFLOW1: pPage is a btree-page. The pointer points at an overflow** page pointed to by one of the cells on pPage.**** PTRMAP_OVERFLOW2: pPage is an overflow-page. The pointer points at the next** overflow page in the list.*/static int modifyPagePointer(MemPage *pPage, Pgno iFrom, Pgno iTo, u8 eType){ if( eType==PTRMAP_OVERFLOW2 ){ /* The pointer is always the first 4 bytes of the page in this case. */ if( get4byte(pPage->aData)!=iFrom ){ return SQLITE_CORRUPT_BKPT; } put4byte(pPage->aData, iTo); }else{ int isInitOrig = pPage->isInit; int i; int nCell; sqlite3BtreeInitPage(pPage, 0); nCell = pPage->nCell; for(i=0; i<nCell; i++){ u8 *pCell = findCell(pPage, i); if( eType==PTRMAP_OVERFLOW1 ){ CellInfo info; sqlite3BtreeParseCellPtr(pPage, pCell, &info); if( info.iOverflow ){ if( iFrom==get4byte(&pCell[info.iOverflow]) ){ put4byte(&pCell[info.iOverflow], iTo); break; } } }else{ if( get4byte(pCell)==iFrom ){ put4byte(pCell, iTo); break; } } } if( i==nCell ){ if( eType!=PTRMAP_BTREE || get4byte(&pPage->aData[pPage->hdrOffset+8])!=iFrom ){ return SQLITE_CORRUPT_BKPT; } put4byte(&pPage->aData[pPage->hdrOffset+8], iTo); } pPage->isInit = isInitOrig; } return SQLITE_OK;}/*** Move the open database page pDbPage to location iFreePage in the ** database. The pDbPage reference remains valid.*/static int relocatePage( BtShared *pBt, /* Btree */ MemPage *pDbPage, /* Open page to move */ u8 eType, /* Pointer map 'type' entry for pDbPage */ Pgno iPtrPage, /* Pointer map 'page-no' entry for pDbPage */ Pgno iFreePage /* The location to move pDbPage to */){ MemPage *pPtrPage; /* The page that contains a pointer to pDbPage */ Pgno iDbPage = pDbPage->pgno; Pager *pPager = pBt->pPager; int rc; assert( eType==PTRMAP_OVERFLOW2 || eType==PTRMAP_OVERFLOW1 || eType==PTRMAP_BTREE || eType==PTRMAP_ROOTPAGE ); /* Move page iDbPage from it's current location to page number iFreePage */ TRACE(("AUTOVACUUM: Moving %d to free page %d (ptr page %d type %d)\n", iDbPage, iFreePage, iPtrPage, eType)); rc = sqlite3PagerMovepage(pPager, pDbPage->pDbPage, iFreePage); if( rc!=SQLITE_OK ){ return rc; } pDbPage->pgno = iFreePage; /* If pDbPage was a btree-page, then it may have child pages and/or cells ** that point to overflow pages. The pointer map entries for all these ** pages need to be changed. ** ** If pDbPage is an overflow page, then the first 4 bytes may store a ** pointer to a subsequent overflow page. If this is the case, then ** the pointer map needs to be updated for the subsequent overflow page. */ if( eType==PTRMAP_BTREE || eType==PTRMAP_ROOTPAGE ){ rc = setChildPtrmaps(pDbPage); if( rc!=SQLITE_OK ){ return rc; } }else{ Pgno nextOvfl = get4byte(pDbPage->aData); if( nextOvfl!=0 ){ rc = ptrmapPut(pBt, nextOvfl, PTRMAP_OVERFLOW2, iFreePage); if( rc!=SQLITE_OK ){ return rc; } } } /* Fix the database pointer on page iPtrPage that pointed at iDbPage so ** that it points at iFreePage. Also fix the pointer map entry for ** iPtrPage. */ if( eType!=PTRMAP_ROOTPAGE ){ rc = sqlite3BtreeGetPage(pBt, iPtrPage, &pPtrPage, 0); if( rc!=SQLITE_OK ){ return rc; } rc = sqlite3PagerWrite(pPtrPage->pDbPage); if( rc!=SQLITE_OK ){ releasePage(pPtrPage); return rc; } rc = modifyPagePointer(pPtrPage, iDbPage, iFreePage, eType); releasePage(pPtrPage); if( rc==SQLITE_OK ){ rc = ptrmapPut(pBt, iFreePage, eType, iPtrPage); } } return rc;}/* Forward declaration required by incrVacuumStep(). */static int allocateBtreePage(BtShared *, MemPage **, Pgno *, Pgno, u8);/*** Perform a single step of an incremental-vacuum. If successful,** return SQLITE_OK. If there is no work to do (and therefore no** point in calling this function again), return SQLITE_DONE.**** More specificly, this function attempts to re-organize the ** database so that the last page of the file currently in use** is no longer in use.**** If the nFin parameter is non-zero, the implementation assumes** that the caller will keep calling incrVacuumStep() until** it returns SQLITE_DONE or an error, and that nFin is the** number of pages the database file will contain after this ** process is complete.*/static int incrVacuumStep(BtShared *pBt, Pgno nFin){ Pgno iLastPg; /* Last page in the database */ Pgno nFreeList; /* Number of pages still on the free-list */ iLastPg = pBt->nTrunc; if( iLastPg==0 ){ iLastPg = sqlite3PagerPagecount(pBt->pPager); } if( !PTRMAP_ISPAGE(pBt, iLastPg) && iLastPg!=PENDING_BYTE_PAGE(pBt) ){ int rc; u8 eType; Pgno iPtrPage; nFreeList = get4byte(&pBt->pPage1->aData[36]); if( nFreeList==0 || nFin==iLastPg ){ return SQLITE_DONE; } rc = ptrmapGet(pBt, iLastPg, &eType, &iPtrPage); if( rc!=SQLITE_OK ){ return rc; } if( eType==PTRMAP_ROOTPAGE ){ return SQLITE_CORRUPT_BKPT; } if( eType==PTRMAP_FREEPAGE ){ if( nFin==0 ){ /* Remove the page from the files free-list. This is not required ** if nFin is non-zero. In that case, the free-list will be ** truncated to zero after this function returns, so it doesn't ** matter if it still contains some garbage entries. */ Pgno iFreePg; MemPage *pFreePg; rc = allocateBtreePage(pBt, &pFreePg, &iFreePg, iLastPg, 1); if( rc!=SQLITE_OK ){ return rc; } assert( iFreePg==iLastPg ); releasePage(pFreePg); } } else { Pgno iFreePg; /* Index of free page to move pLastPg to */ MemPage *pLastPg; rc = sqlite3BtreeGetPage(pBt, iLastPg, &pLastPg, 0); if( rc!=SQLITE_OK ){ return rc; } /* If nFin is zero, this loop runs exactly once and page pLastPg ** is swapped with the first free page pulled off the free list. ** ** On the other hand, if nFin is greater than zero, then keep ** looping until a free-page located within the first nFin pages ** of the file is found. */ do { MemPage *pFreePg; rc = allocateBtreePage(pBt, &pFreePg, &iFreePg, 0, 0); if( rc!=SQLITE_OK ){ releasePage(pLastPg); return rc; } releasePage(pFreePg); }while( nFin!=0 && iFreePg>nFin ); assert( iFreePg<iLastPg ); rc = sqlite3PagerWrite(pLastPg->pDbPage); if( rc!=SQLITE_OK ){ return rc; } rc = relocatePage(pBt, pLastPg, eType, iPtrPage, iFreePg); releasePage(pLastPg); if( rc!=SQLITE_OK ){ return rc; } } } pBt->nTrunc = iLastPg - 1; while( pBt->nTrunc==PENDING_BYTE_PAGE(pBt)||PTRMAP_ISPAGE(pBt, pBt->nTrunc) ){ pBt->nTrunc--; } return SQLITE_OK;}/*** A write-transaction must be opened before calling this function.** It performs a single unit of work towards an incremental vacuum.**** If the incremental vacuum is finished after this function has run,** SQLITE_DONE is returned. If it is not finished, but no error occured,** SQLITE_OK is returned. Otherwise an SQLite error code. */int sqlite3BtreeIncrVacuum(Btree *p){ BtShared *pBt = p->pBt; assert( pBt->inTransaction==TRANS_WRITE && p->inTrans==TRANS_WRITE ); if( !pBt->autoVacuum ){ return SQLITE_DONE; } invalidateAllOverflowCache(pBt); return incrVacuumStep(pBt, 0);}/*** This routine is called prior to sqlite3PagerCommit when a transaction** is commited for an auto-vacuum database.**** If SQLITE_OK is returned, then *pnTrunc is set to the number of pages** the database file should be truncated to during the commit process. ** i.e. the database has been reorganized so that only the first *pnTrunc** pages are in use.*/static int autoVacuumCommit(BtShared *pBt, Pgno *pnTrunc){ int rc = SQLITE_OK; Pager *pPager = pBt->pPager;#ifndef NDEBUG int nRef = sqlite3PagerRefcount(pPager);#endif invalidateAllOverflowCache(pBt); assert(pBt->autoVacuum); if( !pBt->incrVacuum ){ Pgno nFin = 0; if( pBt->nTrunc==0 ){ Pgno nFree; Pgno nPtrmap; const int pgsz = pBt->pageSize; Pgno nOrig = sqlite3PagerPagecount(pBt->pPager); if( PTRMAP_ISPAGE(pBt, nOrig) ){ return SQLITE_CORRUPT_BKPT; } if( nOrig==PENDING_BYTE_PAGE(pBt) ){ nOrig--; } nFree = get4byte(&pBt->pPage1->aData[36]); nPtrmap = (nFree-nOrig+PTRMAP_PAGENO(pBt, nOrig)+pgsz/5)/(pgsz/5); nFin = nOrig - nFree - nPtrmap; if( nOrig>PENDING_BYTE_PAGE(pBt) && nFin<=PENDING_BYTE_PAGE(pBt) ){ nFin--; } while( PTRMAP_ISPAGE(pBt, nFin) || nFin==PENDING_BYTE_PAGE(pBt) ){ nFin--; } } while( rc==SQLITE_OK ){ rc = incrVacuumStep(pBt, nFin); } if( rc==SQLITE_DONE ){ assert(nFin==0 || pBt->nTrunc==0 || nFin<=pBt->nTrunc); rc = SQLITE_OK; if( pBt->nTrunc ){ sqlite3PagerWrite(pBt->pPage1->pDbPage); put4byte(&pBt->pPage1->aData[32], 0); put4byte(&pBt->pPage1->aData[36], 0); pBt->nTrunc = nFin; } } if( rc!=SQLITE_OK ){ sqlite3PagerRollback(pPager); } } if( rc==SQLITE_OK ){ *pnTrunc = pBt->nTrunc; pBt->nTrunc = 0; } assert( nRef==sqlite3PagerRefcount(pPager) ); return rc;}#endif/*** This routine does the first phase of a two-phase commit. This routine** causes a rollback journal to be created (if it does not already exist)** and populated with enough information so that if a power loss occurs** the database can be restored to its original state by playing back** the journal. Then the contents of the journal are flushed out to** the disk. After the journal is safely on oxide, the changes to the** database are written into the database file and flushed to oxide.** At the end of this call, the rollback journal still exists on the** disk and we are still holding all locks, so the transaction has not** committed. See sqlite3BtreeCommit() for the second phase of the** commit process.**** This call is a no-op if no write-transaction is currently active on pBt.**** Otherwise, sync the database file for the btree pBt. zMaster points to** the name of a master journal file that should be written into the** individual journal file, or is NULL, indicating no master journal file ** (single database transaction).**** When this is called, the master journal should already have been** created, populated with this journal pointer and synced to disk.**** Once this is routine has returned, the only thing required to commit** the write-transaction for this database file is to delete the journal.*/int sqlite3BtreeCommitPhaseOne(Btree *p, const char *zMaster){ int rc = SQLITE_OK; if( p->inTrans==TRANS_WRITE ){ BtShared *pBt = p->pBt; Pgno nTrunc = 0;#ifndef SQLITE_OMIT_AUTOVACUUM if( pBt->autoVacuum ){ rc = autoVacuumCommit(pBt, &nTrunc); if( rc!=SQLITE_OK ){ return rc; } }#endif rc = sqlite3PagerCommitPhaseOne(pBt->pPager, zMaster, nTrunc); } return rc;}/*** Commit the transaction currently in progress.**** This routine implements the second phase of a 2-phase commit. The** sqlite3BtreeSync() routine does the first phase and should be invoked** prior to calling this routine. The sqlite3BtreeSync() routine did** all the work of writing information out to disk and flushing the** contents so that they are written onto the disk platter. All this** routine has to do is delete or truncate the rollback journal** (which causes the transaction to commit) and drop locks.**** This will re
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?