📄 btree.c
字号:
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) ); assert( sqlite3_mutex_held(pPage->pBt->mutex) ); 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( 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; decodeFlags(pPage, data[hdr]); 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; } 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; 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); } 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, int pageSize){ MemPage *pPage; assert( (pageSize & 7)==0 ); pPage = (MemPage *)sqlite3PagerGetExtra(pData); 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( (flags & BTREE_PRIVATE)==0 && isMemdb==0 && (db->flags & SQLITE_Vtab)==0 && zFilename && zFilename[0] ){ if( sqlite3SharedCacheEnabled ){ int nFullPathname = pVfs->mxPathname+1; char *zFullPathname = (char *)sqlite3_malloc(nFullPathname); sqlite3_mutex *mutexShared; p->sharable = 1; if( db ){ db->flags |= SQLITE_SharedCache; } if( !zFullPathname ){ sqlite3_free(p); return SQLITE_NOMEM; } sqlite3OsFullPathname(pVfs, zFilename, nFullPathname, zFullPathname); mutexShared = sqlite3_mutex_alloc(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 ); assert( sizeof(Pgno)==4 ); pBt = sqlite3MallocZero( sizeof(*pBt) ); if( pBt==0 ){ rc = SQLITE_NOMEM; goto btree_open_out; } pBt->busyHdr.xFunc = sqlite3BtreeInvokeBusyHandler; pBt->busyHdr.pArg = pBt; rc = sqlite3PagerOpen(pVfs, &pBt->pPager, zFilename, EXTRA_SIZE, flags, vfsFlags); if( rc==SQLITE_OK ){ rc = sqlite3PagerReadFileheader(pBt->pPager,sizeof(zDbHeader),zDbHeader); } if( rc!=SQLITE_OK ){ goto btree_open_out; } sqlite3PagerSetBusyhandler(pBt->pPager, &pBt->busyHdr); p->pBt = pBt; sqlite3PagerSetDestructor(pBt->pPager, pageDestructor); sqlite3PagerSetReiniter(pBt->pPager, pageReinit); pBt->pCursor = 0; pBt->pPage1 = 0; pBt->readOnly = sqlite3PagerIsreadonly(pBt->pPager); pBt->pageSize = get2byte(&zDbHeader[16]); if( pBt->pageSize<512 || pBt->pageSize>SQLITE_MAX_PAGE_SIZE || ((pBt->pageSize-1)&pBt->pageSize)!=0 ){ pBt->pageSize = 0; sqlite3PagerSetPagesize(pBt->pPager, &pBt->pageSize); pBt->maxEmbedFrac = 64; /* 25% */ pBt->minEmbedFrac = 32; /* 12.5% */ pBt->minLeafFrac = 32; /* 12.5% */#ifndef SQLITE_OMIT_AUTOVACUUM /* If the magic name ":memory:" will create an in-memory database, then ** leave the autoVacuum mode at 0 (do not auto-vacuum), even if ** SQLITE_DEFAULT_AUTOVACUUM is true. On the other hand, if ** SQLITE_OMIT_MEMORYDB has been defined, then ":memory:" is just a ** regular file-name. In this case the auto-vacuum applies as per normal. */ if( zFilename && !isMemdb ){ pBt->autoVacuum = (SQLITE_DEFAULT_AUTOVACUUM ? 1 : 0); pBt->incrVacuum = (SQLITE_DEFAULT_AUTOVACUUM==2 ? 1 : 0); }#endif nReserve = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -