⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 btree.c

📁 一个小型的嵌入式数据库
💻 C
📖 第 1 页 / 共 5 页
字号:
  data[20] = pBt->pageSize - pBt->usableSize;  data[21] = pBt->maxEmbedFrac;  data[22] = pBt->minEmbedFrac;  data[23] = pBt->minLeafFrac;  memset(&data[24], 0, 100-24);  zeroPage(pP1, PTF_INTKEY|PTF_LEAF|PTF_LEAFDATA );  pBt->pageSizeFixed = 1;#ifndef SQLITE_OMIT_AUTOVACUUM  if( pBt->autoVacuum ){    put4byte(&data[36 + 4*4], 1);  }#endif  return SQLITE_OK;}/*** Attempt to start a new transaction. A write-transaction** is started if the second argument is nonzero, otherwise a read-** transaction.  If the second argument is 2 or more and exclusive** transaction is started, meaning that no other process is allowed** to access the database.  A preexisting transaction may not be** upgrade to exclusive by calling this routine a second time - the** exclusivity flag only works for a new transaction.**** A write-transaction must be started before attempting any ** changes to the database.  None of the following routines ** will work unless a transaction is started first:****      sqlite3BtreeCreateTable()**      sqlite3BtreeCreateIndex()**      sqlite3BtreeClearTable()**      sqlite3BtreeDropTable()**      sqlite3BtreeInsert()**      sqlite3BtreeDelete()**      sqlite3BtreeUpdateMeta()**** If wrflag is true, then nMaster specifies the maximum length of** a master journal file name supplied later via sqlite3BtreeSync().** This is so that appropriate space can be allocated in the journal file** when it is created..*/int sqlite3BtreeBeginTrans(Btree *pBt, int wrflag){  int rc = SQLITE_OK;  /* If the btree is already in a write-transaction, or it  ** is already in a read-transaction and a read-transaction  ** is requested, this is a no-op.  */  if( pBt->inTrans==TRANS_WRITE ||       (pBt->inTrans==TRANS_READ && !wrflag) ){    return SQLITE_OK;  }  if( pBt->readOnly && wrflag ){    return SQLITE_READONLY;  }  if( pBt->pPage1==0 ){    rc = lockBtree(pBt);  }  if( rc==SQLITE_OK && wrflag ){    rc = sqlite3pager_begin(pBt->pPage1->aData, wrflag>1);    if( rc==SQLITE_OK ){      rc = newDatabase(pBt);    }  }  if( rc==SQLITE_OK ){    pBt->inTrans = (wrflag?TRANS_WRITE:TRANS_READ);    if( wrflag ) pBt->inStmt = 0;  }else{    unlockBtreeIfUnused(pBt);  }  return rc;}#ifndef SQLITE_OMIT_AUTOVACUUM/*** Set the pointer-map entries for all children of page pPage. Also, if** pPage contains cells that point to overflow pages, set the pointer** map entries for the overflow pages as well.*/static int setChildPtrmaps(MemPage *pPage){  int i;                             /* Counter variable */  int nCell;                         /* Number of cells in page pPage */  int rc = SQLITE_OK;                /* Return code */  Btree *pBt = pPage->pBt;  int isInitOrig = pPage->isInit;  Pgno pgno = pPage->pgno;  initPage(pPage, 0);  nCell = pPage->nCell;  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;    }    put4byte(pPage->aData, iTo);  }else{    int isInitOrig = pPage->isInit;    int i;    int nCell;    initPage(pPage, 0);    nCell = pPage->nCell;    for(i=0; i<nCell; i++){      u8 *pCell = findCell(pPage, i);      if( eType==PTRMAP_OVERFLOW1 ){        CellInfo info;        parseCellPtr(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;      }      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(  Btree *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 = sqlite3pager_movepage(pPager, pDbPage->aData, 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 = getPage(pBt, iPtrPage, &pPtrPage);    if( rc!=SQLITE_OK ){      return rc;    }    rc = sqlite3pager_write(pPtrPage->aData);    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 autoVacuumCommit(). */static int allocatePage(Btree *, MemPage **, Pgno *, Pgno, u8);/*** This routine is called prior to sqlite3pager_commit when a transaction** is commited for an auto-vacuum database.*/static int autoVacuumCommit(Btree *pBt, Pgno *nTrunc){  Pager *pPager = pBt->pPager;  Pgno nFreeList;   /* Number of pages remaining on the free-list. */  int nPtrMap;      /* Number of pointer-map pages deallocated */  Pgno origSize;  /* Pages in the database file */  Pgno finSize;   /* Pages in the database file after truncation */  int rc;           /* Return code */  u8 eType;  int pgsz = pBt->pageSize;  /* Page size for this database */  Pgno iDbPage;              /* The database page to move */  MemPage *pDbMemPage = 0;   /* "" */  Pgno iPtrPage;             /* The page that contains a pointer to iDbPage */  Pgno iFreePage;            /* The free-list page to move iDbPage to */  MemPage *pFreeMemPage = 0; /* "" */#ifndef NDEBUG  int nRef = *sqlite3pager_stats(pPager);#endif  assert( pBt->autoVacuum );  if( PTRMAP_ISPAGE(pgsz, sqlite3pager_pagecount(pPager)) ){    return SQLITE_CORRUPT;  }  /* Figure out how many free-pages are in the database. If there are no  ** free pages, then auto-vacuum is a no-op.  */  nFreeList = get4byte(&pBt->pPage1->aData[36]);  if( nFreeList==0 ){    *nTrunc = 0;    return SQLITE_OK;  }  origSize = sqlite3pager_pagecount(pPager);  nPtrMap = (nFreeList-origSize+PTRMAP_PAGENO(pgsz, origSize)+pgsz/5)/(pgsz/5);  finSize = origSize - nFreeList - nPtrMap;  if( origSize>PENDING_BYTE_PAGE(pBt) && finSize<=PENDING_BYTE_PAGE(pBt) ){    finSize--;    if( PTRMAP_ISPAGE(pBt->usableSize, finSize) ){      finSize--;    }  }  TRACE(("AUTOVACUUM: Begin (db size %d->%d)\n", origSize, finSize));  /* Variable 'finSize' will be the size of the file in pages after  ** the auto-vacuum has completed (the current file size minus the number  ** of pages on the free list). Loop through the pages that lie beyond  ** this mark, and if they are not already on the free list, move them  ** to a free page earlier in the file (somewhere before finSize).  */  for( iDbPage=finSize+1; iDbPage<=origSize; iDbPage++ ){    /* If iDbPage is a pointer map page, or the pending-byte page, skip it. */    if( PTRMAP_ISPAGE(pgsz, iDbPage) || iDbPage==PENDING_BYTE_PAGE(pBt) ){      continue;    }    rc = ptrmapGet(pBt, iDbPage, &eType, &iPtrPage);    if( rc!=SQLITE_OK ) goto autovacuum_out;    assert( eType!=PTRMAP_ROOTPAGE );    /* If iDbPage is free, do not swap it.  */    if( eType==PTRMAP_FREEPAGE ){      continue;    }    rc = getPage(pBt, iDbPage, &pDbMemPage);    if( rc!=SQLITE_OK ) goto autovacuum_out;    /* Find the next page in the free-list that is not already at the end     ** of the file. A page can be pulled off the free list using the     ** allocatePage() routine.    */    do{      if( pFreeMemPage ){        releasePage(pFreeMemPage);        pFreeMemPage = 0;      }      rc = allocatePage(pBt, &pFreeMemPage, &iFreePage, 0, 0);      if( rc!=SQLITE_OK ){        releasePage(pDbMemPage);        goto autovacuum_out;      }      assert( iFreePage<=origSize );    }while( iFreePage>finSize );    releasePage(pFreeMemPage);    pFreeMemPage = 0;    rc = relocatePage(pBt, pDbMemPage, eType, iPtrPage, iFreePage);    releasePage(pDbMemPage);    if( rc!=SQLITE_OK ) goto autovacuum_out;  }  /* The entire free-list has been swapped to the end of the file. So  ** truncate the database file to finSize pages and consider the  ** free-list empty.  */  rc = sqlite3pager_write(pBt->pPage1->aData);  if( rc!=SQLITE_OK ) goto autovacuum_out;  put4byte(&pBt->pPage1->aData[32], 0);  put4byte(&pBt->pPage1->aData[36], 0);  if( rc!=SQLITE_OK ) goto autovacuum_out;  *nTrunc = finSize;autovacuum_out:  assert( nRef==*sqlite3pager_stats(pPager) );  if( rc!=SQLITE_OK ){    sqlite3pager_rollback(pPager);  }  return rc;}#endif/*** Commit the transaction currently in progress.**** This will release the write lock on the database file.  If there** are no active curso

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -