btree.c

来自「sqlite-3.4.1,嵌入式数据库.是一个功能强大的开源数据库,给学习和研发」· C语言 代码 · 共 2,088 行 · 第 1/5 页

C
2,088
字号
**** This routine works only for pages that do not contain overflow cells.*/#define findCell(pPage, iCell) \  ((pPage)->aData + get2byte(&(pPage)->aData[(pPage)->cellOffset+2*(iCell)]))u8 *sqlite3BtreeFindCell(MemPage *pPage, int iCell){  assert( iCell>=0 );  assert( iCell<get2byte(&pPage->aData[pPage->hdrOffset+3]) );  return findCell(pPage, iCell);}/*** This a more complex version of sqlite3BtreeFindCell() that works for** pages that do contain overflow cells.  See insert*/static u8 *findOverflowCell(MemPage *pPage, int iCell){  int i;  for(i=pPage->nOverflow-1; i>=0; i--){    int k;    struct _OvflCell *pOvfl;    pOvfl = &pPage->aOvfl[i];    k = pOvfl->idx;    if( k<=iCell ){      if( k==iCell ){        return pOvfl->pCell;      }      iCell--;    }  }  return findCell(pPage, iCell);}/*** Parse a cell content block and fill in the CellInfo structure.  There** are two versions of this function.  sqlite3BtreeParseCell() takes a ** cell index as the second argument and sqlite3BtreeParseCellPtr() ** takes a pointer to the body of the cell as its second argument.**** Within this file, the parseCell() macro can be called instead of** sqlite3BtreeParseCellPtr(). Using some compilers, this will be faster.*/void sqlite3BtreeParseCellPtr(  MemPage *pPage,         /* Page containing the cell */  u8 *pCell,              /* Pointer to the cell text. */  CellInfo *pInfo         /* Fill in this structure */){  int n;                  /* Number bytes in cell content header */  u32 nPayload;           /* Number of bytes of cell payload */  pInfo->pCell = pCell;  assert( pPage->leaf==0 || pPage->leaf==1 );  n = pPage->childPtrSize;  assert( n==4-4*pPage->leaf );  if( pPage->hasData ){    n += getVarint32(&pCell[n], &nPayload);  }else{    nPayload = 0;  }  pInfo->nData = nPayload;  if( pPage->intKey ){    n += getVarint(&pCell[n], (u64 *)&pInfo->nKey);  }else{    u32 x;    n += getVarint32(&pCell[n], &x);    pInfo->nKey = x;    nPayload += x;  }  pInfo->nPayload = nPayload;  pInfo->nHeader = n;  if( nPayload<=pPage->maxLocal ){    /* This is the (easy) common case where the entire payload fits    ** on the local page.  No overflow is required.    */    int nSize;          /* Total size of cell content in bytes */    pInfo->nLocal = nPayload;    pInfo->iOverflow = 0;    nSize = nPayload + n;    if( nSize<4 ){      nSize = 4;        /* Minimum cell size is 4 */    }    pInfo->nSize = nSize;  }else{    /* If the payload will not fit completely on the local page, we have    ** to decide how much to store locally and how much to spill onto    ** overflow pages.  The strategy is to minimize the amount of unused    ** space on overflow pages while keeping the amount of local storage    ** in between minLocal and maxLocal.    **    ** Warning:  changing the way overflow payload is distributed in any    ** way will result in an incompatible file format.    */    int minLocal;  /* Minimum amount of payload held locally */    int maxLocal;  /* Maximum amount of payload held locally */    int surplus;   /* Overflow payload available for local storage */    minLocal = pPage->minLocal;    maxLocal = pPage->maxLocal;    surplus = minLocal + (nPayload - minLocal)%(pPage->pBt->usableSize - 4);    if( surplus <= maxLocal ){      pInfo->nLocal = surplus;    }else{      pInfo->nLocal = minLocal;    }    pInfo->iOverflow = pInfo->nLocal + n;    pInfo->nSize = pInfo->iOverflow + 4;  }}#define parseCell(pPage, iCell, pInfo) \  sqlite3BtreeParseCellPtr((pPage), findCell((pPage), (iCell)), (pInfo))void sqlite3BtreeParseCell(  MemPage *pPage,         /* Page containing the cell */  int iCell,              /* The cell index.  First cell is 0 */  CellInfo *pInfo         /* Fill in this structure */){  parseCell(pPage, iCell, pInfo);}/*** Compute the total number of bytes that a Cell needs in the cell** data area of the btree-page.  The return number includes the cell** data header and the local payload, but not any overflow page or** the space used by the cell pointer.*/#ifndef NDEBUGstatic int cellSize(MemPage *pPage, int iCell){  CellInfo info;  sqlite3BtreeParseCell(pPage, iCell, &info);  return info.nSize;}#endifstatic int cellSizePtr(MemPage *pPage, u8 *pCell){  CellInfo info;  sqlite3BtreeParseCellPtr(pPage, pCell, &info);  return info.nSize;}#ifndef SQLITE_OMIT_AUTOVACUUM/*** If the cell pCell, part of page pPage contains a pointer** to an overflow page, insert an entry into the pointer-map** for the overflow page.*/static int ptrmapPutOvflPtr(MemPage *pPage, u8 *pCell){  if( pCell ){    CellInfo info;    sqlite3BtreeParseCellPtr(pPage, pCell, &info);    assert( (info.nData+(pPage->intKey?0:info.nKey))==info.nPayload );    if( (info.nData+(pPage->intKey?0:info.nKey))>info.nLocal ){      Pgno ovfl = get4byte(&pCell[info.iOverflow]);      return ptrmapPut(pPage->pBt, ovfl, PTRMAP_OVERFLOW1, pPage->pgno);    }  }  return SQLITE_OK;}/*** If the cell with index iCell on page pPage contains a pointer** to an overflow page, insert an entry into the pointer-map** for the overflow page.*/static int ptrmapPutOvfl(MemPage *pPage, int iCell){  u8 *pCell;  pCell = findOverflowCell(pPage, iCell);  return ptrmapPutOvflPtr(pPage, pCell);}#endif/*** Defragment the page given.  All Cells are moved to the** end of the page and all free space is collected into one** big FreeBlk that occurs in between the header and cell** pointer array and the cell content area.*/static int defragmentPage(MemPage *pPage){  int i;                     /* Loop counter */  int pc;                    /* Address of a i-th cell */  int addr;                  /* Offset of first byte after cell pointer array */  int hdr;                   /* Offset to the page header */  int size;                  /* Size of a cell */  int usableSize;            /* Number of usable bytes on a page */  int cellOffset;            /* Offset to the cell pointer array */  int brk;                   /* Offset to the cell content area */  int nCell;                 /* Number of cells on the page */  unsigned char *data;       /* The page data */  unsigned char *temp;       /* Temp area for cell content */  assert( sqlite3PagerIswriteable(pPage->pDbPage) );  assert( pPage->pBt!=0 );  assert( pPage->pBt->usableSize <= SQLITE_MAX_PAGE_SIZE );  assert( pPage->nOverflow==0 );  temp = sqliteMalloc( pPage->pBt->pageSize );  if( temp==0 ) return SQLITE_NOMEM;  data = pPage->aData;  hdr = pPage->hdrOffset;  cellOffset = pPage->cellOffset;  nCell = pPage->nCell;  assert( nCell==get2byte(&data[hdr+3]) );  usableSize = pPage->pBt->usableSize;  brk = get2byte(&data[hdr+5]);  memcpy(&temp[brk], &data[brk], usableSize - brk);  brk = usableSize;  for(i=0; i<nCell; i++){    u8 *pAddr;     /* The i-th cell pointer */    pAddr = &data[cellOffset + i*2];    pc = get2byte(pAddr);    assert( pc<pPage->pBt->usableSize );    size = cellSizePtr(pPage, &temp[pc]);    brk -= size;    memcpy(&data[brk], &temp[pc], size);    put2byte(pAddr, brk);  }  assert( brk>=cellOffset+2*nCell );  put2byte(&data[hdr+5], brk);  data[hdr+1] = 0;  data[hdr+2] = 0;  data[hdr+7] = 0;  addr = cellOffset+2*nCell;  memset(&data[addr], 0, brk-addr);  sqliteFree(temp);  return SQLITE_OK;}/*** Allocate nByte bytes of space on a page.**** Return the index into pPage->aData[] of the first byte of** the new allocation. Or return 0 if there is not enough free** space on the page to satisfy the allocation request.**** If the page contains nBytes of free space but does not contain** nBytes of contiguous free space, then this routine automatically** calls defragementPage() to consolidate all free space before ** allocating the new chunk.*/static int allocateSpace(MemPage *pPage, int nByte){  int addr, pc, hdr;  int size;  int nFrag;  int top;  int nCell;  int cellOffset;  unsigned char *data;    data = pPage->aData;  assert( sqlite3PagerIswriteable(pPage->pDbPage) );  assert( pPage->pBt );  if( nByte<4 ) nByte = 4;  if( pPage->nFree<nByte || pPage->nOverflow>0 ) return 0;  pPage->nFree -= nByte;  hdr = pPage->hdrOffset;  nFrag = data[hdr+7];  if( nFrag<60 ){    /* Search the freelist looking for a slot big enough to satisfy the    ** space request. */    addr = hdr+1;    while( (pc = get2byte(&data[addr]))>0 ){      size = get2byte(&data[pc+2]);      if( size>=nByte ){        if( size<nByte+4 ){          memcpy(&data[addr], &data[pc], 2);          data[hdr+7] = nFrag + size - nByte;          return pc;        }else{          put2byte(&data[pc+2], size-nByte);          return pc + size - nByte;        }      }      addr = pc;    }  }  /* Allocate memory from the gap in between the cell pointer array  ** and the cell content area.  */  top = get2byte(&data[hdr+5]);  nCell = get2byte(&data[hdr+3]);  cellOffset = pPage->cellOffset;  if( nFrag>=60 || cellOffset + 2*nCell > top - nByte ){    if( defragmentPage(pPage) ) return 0;    top = get2byte(&data[hdr+5]);  }  top -= nByte;  assert( cellOffset + 2*nCell <= top );  put2byte(&data[hdr+5], top);  return top;}/*** Return a section of the pPage->aData to the freelist.** The first byte of the new free block is pPage->aDisk[start]** and the size of the block is "size" bytes.**** Most of the effort here is involved in coalesing adjacent** free blocks into a single big free block.*/static void freeSpace(MemPage *pPage, int start, int size){  int addr, pbegin, hdr;  unsigned char *data = pPage->aData;  assert( pPage->pBt!=0 );  assert( sqlite3PagerIswriteable(pPage->pDbPage) );  assert( start>=pPage->hdrOffset+6+(pPage->leaf?0:4) );  assert( (start + size)<=pPage->pBt->usableSize );  if( size<4 ) size = 4;#ifdef SQLITE_SECURE_DELETE  /* Overwrite deleted information with zeros when the SECURE_DELETE   ** option is enabled at compile-time */  memset(&data[start], 0, size);#endif  /* Add the space back into the linked list of freeblocks */  hdr = pPage->hdrOffset;  addr = hdr + 1;  while( (pbegin = get2byte(&data[addr]))<start && pbegin>0 ){    assert( pbegin<=pPage->pBt->usableSize-4 );    assert( pbegin>addr );    addr = pbegin;  }  assert( pbegin<=pPage->pBt->usableSize-4 );  assert( pbegin>addr || pbegin==0 );  put2byte(&data[addr], start);  put2byte(&data[start], pbegin);  put2byte(&data[start+2], size);  pPage->nFree += size;  /* Coalesce adjacent free blocks */  addr = pPage->hdrOffset + 1;  while( (pbegin = get2byte(&data[addr]))>0 ){    int pnext, psize;    assert( pbegin>addr );    assert( pbegin<=pPage->pBt->usableSize-4 );    pnext = get2byte(&data[pbegin]);    psize = get2byte(&data[pbegin+2]);    if( pbegin + psize + 3 >= pnext && pnext>0 ){      int frag = pnext - (pbegin+psize);      assert( frag<=data[pPage->hdrOffset+7] );      data[pPage->hdrOffset+7] -= frag;      put2byte(&data[pbegin], get2byte(&data[pnext]));      put2byte(&data[pbegin+2], pnext+get2byte(&data[pnext+2])-pbegin);    }else{      addr = pbegin;    }  }  /* If the cell content area begins with a freeblock, remove it. */  if( data[hdr+1]==data[hdr+5] && data[hdr+2]==data[hdr+6] ){    int top;    pbegin = get2byte(&data[hdr+1]);    memcpy(&data[hdr+1], &data[pbegin], 2);    top = get2byte(&data[hdr+5]);    put2byte(&data[hdr+5], top + get2byte(&data[pbegin+2]));  }}/*** Decode the flags byte (the first byte of the header) for a page** and initialize fields of the MemPage structure accordingly.*/static void decodeFlags(MemPage *pPage, int flagByte){  BtShared *pBt;     /* A copy of pPage->pBt */  assert( pPage->hdrOffset==(pPage->pgno==1 ? 100 : 0) );  pPage->intKey = (flagByte & (PTF_INTKEY|PTF_LEAFDATA))!=0;  pPage->zeroData = (flagByte & PTF_ZERODATA)!=0;  pPage->leaf = (flagByte & PTF_LEAF)!=0;  pPage->childPtrSize = 4*(pPage->leaf==0);  pBt = pPage->pBt;  if( flagByte & PTF_LEAFDATA ){    pPage->leafData = 1;    pPage->maxLocal = pBt->maxLeaf;    pPage->minLocal = pBt->minLeaf;  }else{    pPage->leafData = 0;    pPage->maxLocal = pBt->maxLocal;    pPage->minLocal = pBt->minLocal;  }  pPage->hasData = !(pPage->zeroData || (!pPage->leaf && pPage->leafData));}/*** Initialize the auxiliary information for a disk block.**** The pParent parameter must be a pointer to the MemPage which** is the parent of the page being initialized.  The root of a** BTree has no parent and so for that page, pParent==NULL.**** Return SQLITE_OK on success.  If we see that the page does** not contain a well-formed database page, then return ** SQLITE_CORRUPT.  Note that a return of SQLITE_OK does not** guarantee that the page is well-formed.  It only shows that** we failed to detect any corruption.*/int sqlite3BtreeInitPage(  MemPage *pPage,        /* The page to be initialized */  MemPage *pParent       /* The parent.  Might be NULL */){  int pc;            /* Address of a freeblock within pPage->aData[] */  int hdr;           /* Offset to beginning of page header */  u8 *data;          /* Equal to pPage->aData */  BtShared *pBt;        /* The main btree structure */  int usableSize;    /* Amount of usable space on each page */  int cellOffset;    /* Offset from start of page to first cell pointer */  int nFree;         /* Number of unused bytes on the page */  int top;           /* First byte of the cell content area */  pBt = pPage->pBt;  assert( pBt!=0 );  assert( pParent==0 || pParent->pBt==pBt );  assert( pPage->pgno==sqlite3PagerPagenumber(pPage->pDbPage) );  assert( pPage->aData == &((unsigned char*)pPage)[-pBt->pageSize] );  if( pPage->pParent!=pParent && (pPage->pParent!=0 || pPage->isInit) ){    /* The parent page should never change unless the file is corrupt */    return SQLITE_CORRUPT_BKPT;  }  if( pPage->isInit ) return SQLITE_OK;  if( pPage->pParent==0 && pParent!=0 ){

⌨️ 快捷键说明

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