btree.c

来自「最新的sqlite3.6.2源代码」· C语言 代码 · 共 2,132 行 · 第 1/5 页

C
2,132
字号
      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.**** Only the following combinations are supported.  Anything different** indicates a corrupt database files:****         PTF_ZERODATA**         PTF_ZERODATA | PTF_LEAF**         PTF_LEAFDATA | PTF_INTKEY**         PTF_LEAFDATA | PTF_INTKEY | PTF_LEAF*/static int decodeFlags(MemPage *pPage, int flagByte){  BtShared *pBt;     /* A copy of pPage->pBt */  assert( pPage->hdrOffset==(pPage->pgno==1 ? 100 : 0) );  assert( sqlite3_mutex_held(pPage->pBt->mutex) );  pPage->leaf = flagByte>>3;  assert( PTF_LEAF == 1<<3 );  flagByte &= ~PTF_LEAF;  pPage->childPtrSize = 4-4*pPage->leaf;  pBt = pPage->pBt;  if( flagByte==(PTF_LEAFDATA | PTF_INTKEY) ){    pPage->intKey = 1;    pPage->hasData = pPage->leaf;    pPage->maxLocal = pBt->maxLeaf;    pPage->minLocal = pBt->minLeaf;  }else if( flagByte==PTF_ZERODATA ){    pPage->intKey = 0;    pPage->hasData = 0;    pPage->maxLocal = pBt->maxLocal;    pPage->minLocal = pBt->minLocal;  }else{    return SQLITE_CORRUPT_BKPT;  }  return SQLITE_OK;}/*** 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( sqlite3_mutex_held(pBt->mutex) );  assert( pPage->pgno==sqlite3PagerPagenumber(pPage->pDbPage) );  assert( pPage == sqlite3PagerGetExtra(pPage->pDbPage) );  assert( pPage->aData == sqlite3PagerGetData(pPage->pDbPage) );  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 ){    pPage->pParent = pParent;    sqlite3PagerRef(pParent->pDbPage);  }  hdr = pPage->hdrOffset;  data = pPage->aData;  if( decodeFlags(pPage, data[hdr]) ) return SQLITE_CORRUPT_BKPT;  assert( pBt->pageSize>=512 && pBt->pageSize<=32768 );  pPage->maskPage = pBt->pageSize - 1;  pPage->nOverflow = 0;  pPage->idxShift = 0;  usableSize = pBt->usableSize;  pPage->cellOffset = cellOffset = hdr + 12 - 4*pPage->leaf;  top = get2byte(&data[hdr+5]);  pPage->nCell = get2byte(&data[hdr+3]);  if( pPage->nCell>MX_CELL(pBt) ){    /* To many cells for a single page.  The page must be corrupt */    return SQLITE_CORRUPT_BKPT;  }  if( pPage->nCell==0 && pParent!=0 && pParent->pgno!=1 ){    /* All pages must have at least one cell, except for root pages */    return SQLITE_CORRUPT_BKPT;  }  /* Compute the total free space on the page */  pc = get2byte(&data[hdr+1]);  nFree = data[hdr+7] + top - (cellOffset + 2*pPage->nCell);  while( pc>0 ){    int next, size;    if( pc>usableSize-4 ){      /* Free block is off the page */      return SQLITE_CORRUPT_BKPT;     }    next = get2byte(&data[pc]);    size = get2byte(&data[pc+2]);    if( next>0 && next<=pc+size+3 ){      /* Free blocks must be in accending order */      return SQLITE_CORRUPT_BKPT;     }    nFree += size;    pc = next;  }  pPage->nFree = nFree;  if( nFree>=usableSize ){    /* Free space cannot exceed total page size */    return SQLITE_CORRUPT_BKPT;   }#if 0  /* Check that all the offsets in the cell offset array are within range.   **   ** Omitting this consistency check and using the pPage->maskPage mask  ** to prevent overrunning the page buffer in findCell() results in a  ** 2.5% performance gain.  */  {    u8 *pOff;        /* Iterator used to check all cell offsets are in range */    u8 *pEnd;        /* Pointer to end of cell offset array */    u8 mask;         /* Mask of bits that must be zero in MSB of cell offsets */    mask = ~(((u8)(pBt->pageSize>>8))-1);    pEnd = &data[cellOffset + pPage->nCell*2];    for(pOff=&data[cellOffset]; pOff!=pEnd && !((*pOff)&mask); pOff+=2);    if( pOff!=pEnd ){      return SQLITE_CORRUPT_BKPT;    }  }#endif  pPage->isInit = 1;  return SQLITE_OK;}/*** Set up a raw page so that it looks like a database page holding** no entries.*/static void zeroPage(MemPage *pPage, int flags){  unsigned char *data = pPage->aData;  BtShared *pBt = pPage->pBt;  int hdr = pPage->hdrOffset;  int first;  assert( sqlite3PagerPagenumber(pPage->pDbPage)==pPage->pgno );  assert( sqlite3PagerGetExtra(pPage->pDbPage) == (void*)pPage );  assert( sqlite3PagerGetData(pPage->pDbPage) == data );  assert( sqlite3PagerIswriteable(pPage->pDbPage) );  assert( sqlite3_mutex_held(pBt->mutex) );  /*memset(&data[hdr], 0, pBt->usableSize - hdr);*/  data[hdr] = flags;  first = hdr + 8 + 4*((flags&PTF_LEAF)==0);  memset(&data[hdr+1], 0, 4);  data[hdr+7] = 0;  put2byte(&data[hdr+5], pBt->usableSize);  pPage->nFree = pBt->usableSize - first;  decodeFlags(pPage, flags);  pPage->hdrOffset = hdr;  pPage->cellOffset = first;  pPage->nOverflow = 0;  assert( pBt->pageSize>=512 && pBt->pageSize<=32768 );  pPage->maskPage = pBt->pageSize - 1;  pPage->idxShift = 0;  pPage->nCell = 0;  pPage->isInit = 1;}/*** Get a page from the pager.  Initialize the MemPage.pBt and** MemPage.aData elements if needed.**** If the noContent flag is set, it means that we do not care about** the content of the page at this time.  So do not go to the disk** to fetch the content.  Just fill in the content with zeros for now.** If in the future we call sqlite3PagerWrite() on this page, that** means we have started to be concerned about content and the disk** read should occur at that point.*/int sqlite3BtreeGetPage(  BtShared *pBt,       /* The btree */  Pgno pgno,           /* Number of the page to fetch */  MemPage **ppPage,    /* Return the page in this parameter */  int noContent        /* Do not load page content if true */){  int rc;  MemPage *pPage;  DbPage *pDbPage;  assert( sqlite3_mutex_held(pBt->mutex) );  rc = sqlite3PagerAcquire(pBt->pPager, pgno, (DbPage**)&pDbPage, noContent);  if( rc ) return rc;  pPage = (MemPage *)sqlite3PagerGetExtra(pDbPage);  pPage->aData = sqlite3PagerGetData(pDbPage);  pPage->pDbPage = pDbPage;  pPage->pBt = pBt;  pPage->pgno = pgno;  pPage->hdrOffset = pPage->pgno==1 ? 100 : 0;  *ppPage = pPage;  return SQLITE_OK;}/*** Get a page from the pager and initialize it.  This routine** is just a convenience wrapper around separate calls to** sqlite3BtreeGetPage() and sqlite3BtreeInitPage().*/static int getAndInitPage(  BtShared *pBt,          /* The database file */  Pgno pgno,           /* Number of the page to get */  MemPage **ppPage,    /* Write the page pointer here */  MemPage *pParent     /* Parent of the page */){  int rc;  assert( sqlite3_mutex_held(pBt->mutex) );  if( pgno==0 ){    return SQLITE_CORRUPT_BKPT;   }  rc = sqlite3BtreeGetPage(pBt, pgno, ppPage, 0);  if( rc==SQLITE_OK && (*ppPage)->isInit==0 ){    rc = sqlite3BtreeInitPage(*ppPage, pParent);    if( rc!=SQLITE_OK ){      releasePage(*ppPage);      *ppPage = 0;    }  }  return rc;}/*** Release a MemPage.  This should be called once for each prior** call to sqlite3BtreeGetPage.*/static void releasePage(MemPage *pPage){  if( pPage ){    assert( pPage->aData );    assert( pPage->pBt );    assert( sqlite3PagerGetExtra(pPage->pDbPage) == (void*)pPage );    assert( sqlite3PagerGetData(pPage->pDbPage)==pPage->aData );    assert( sqlite3_mutex_held(pPage->pBt->mutex) );    sqlite3PagerUnref(pPage->pDbPage);  }}/*** This routine is called when the reference count for a page** reaches zero.  We need to unref the pParent pointer when that** happens.*/static void pageDestructor(DbPage *pData){  MemPage *pPage;  pPage = (MemPage *)sqlite3PagerGetExtra(pData);  if( pPage ){    assert( pPage->isInit==0 || sqlite3_mutex_held(pPage->pBt->mutex) );    if( pPage->pParent ){      MemPage *pParent = pPage->pParent;      assert( pParent->pBt==pPage->pBt );      pPage->pParent = 0;      releasePage(pParent);    }    pPage->isInit = 0;  }}/*** During a rollback, when the pager reloads information into the cache** so that the cache is restored to its original state at the start of** the transaction, for each page restored this routine is called.**** This routine needs to reset the extra data section at the end of the** page to agree with the restored data.*/static void pageReinit(DbPage *pData, int pageSize){  MemPage *pPage;  assert( (pageSize & 7)==0 );  pPage = (MemPage *)sqlite3PagerGetExtra(pData);  if( pPage->isInit ){    assert( sqlite3_mutex_held(pPage->pBt->mutex) );    pPage->isInit = 0;    sqlite3BtreeInitPage(pPage, pPage->pParent);  }}/*** Invoke the busy handler for a btree.*/static int sqlite3BtreeInvokeBusyHandler(void *pArg, int n){  BtShared *pBt = (BtShared*)pArg;  assert( pBt->db );  assert( sqlite3_mutex_held(pBt->db->mutex) );  return sqlite3InvokeBusyHandler(&pBt->db->busyHandler);}/*** Open a database file.** ** zFilename is the name of the database file.  If zFilename is NULL** a new database with a random name is created.  This randomly named** database file will be deleted when sqlite3BtreeClose() is called.** If zFilename is ":memory:" then an in-memory database is created** that is automatically destroyed when it is closed.*/int sqlite3BtreeOpen(  const char *zFilename,  /* Name of the file containing the BTree database */  sqlite3 *db,            /* Associated database handle */  Btree **ppBtree,        /* Pointer to new Btree object written here */  int flags,              /* Options */  int vfsFlags            /* Flags passed through to sqlite3_vfs.xOpen() */){  sqlite3_vfs *pVfs;      /* The VFS to use for this btree */  BtShared *pBt = 0;      /* Shared part of btree structure */  Btree *p;               /* Handle to return */  int rc = SQLITE_OK;  int nReserve;  unsigned char zDbHeader[100];  /* Set the variable isMemdb to true for an in-memory database, or   ** false for a file-based database. This symbol is only required if  ** either of the shared-data or autovacuum features are compiled   ** into the library.  */#if !defined(SQLITE_OMIT_SHARED_CACHE) || !defined(SQLITE_OMIT_AUTOVACUUM)  #ifdef SQLITE_OMIT_MEMORYDB    const int isMemdb = 0;  #else    const int isMemdb = zFilename && !strcmp(zFilename, ":memory:");  #endif#endif  assert( db!=0 );  assert( sqlite3_mutex_held(db->mutex) );  pVfs = db->pVfs;  p = sqlite3MallocZero(sizeof(Btree));  if( !p ){    return SQLITE_NOMEM;  }  p->inTrans = TRANS_NONE;  p->db = db;#if !defined(SQLITE_OMIT_SHARED_CACHE) && !defined(SQLITE_OMIT_DISKIO)  /*  ** If this Btree is a candidate for shared cache, try to find an  ** existing BtShared object that we can share with  */  if( isMemdb==0   && (db->flags & SQLITE_Vtab)==0   && zFilename && zFilename[0]  ){    if( sqlite3SharedCacheEnabled ){      int nFullPathname = pVfs->mxPathname+1;      char *zFullPathname = sqlite3Malloc(nFullPathname);      sqlite3_mutex *mutexShared;      p->sharable = 1;      db->flags |= SQLITE_SharedCache;      if( !zFullPathname ){        sqlite3_free(p);        return SQLITE_NOMEM;      }      sqlite3OsFullPathname(pVfs, zFilename, nFullPathname, zFullPathname);      mutexShared = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);      sqlite3_mutex_enter(mutexShared);      for(pBt=sqlite3SharedCacheList; pBt; pBt=pBt->pNext){        assert( pBt->nRef>0 );        if( 0==strcmp(zFullPathname, sqlite3PagerFilename(pBt->pPager))                 && sqlite3PagerVfs(pBt->pPager)==pVfs ){          p->pBt = pBt;          pBt->nRef++;          break;        }      }      sqlite3_mutex_leave(mutexShared);      sqlite3_free(zFullPathname);    }#ifdef SQLITE_DEBUG    else{      /* In debug mode, we mark all persistent databases as sharable      ** even when they are not.  This exercises the locking code and      ** gives more opportunity for asserts(sqlite3_mutex_held())      ** statements to find locking problems.      */      p->sharable = 1;    }#endif  }#endif  if( pBt==0 ){    /*    ** The following asserts make sure that structures used by the btree are    ** the right size.  This is to guard against size changes that result    ** when compiling on a different architecture.    */    assert( sizeof(i64)==8 || sizeof(i64)==4 );    assert( sizeof(u64)==8 || sizeof(u64)==4 );    assert( sizeof(u32)==4 );    assert( sizeof(u16)==2 );

⌨️ 快捷键说明

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