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 + -
显示快捷键?