📄 btree.c
字号:
** parameter is non-zero, then auto-vacuum mode is enabled. If zero, it** is disabled. The default value for the auto-vacuum property is ** determined by the SQLITE_DEFAULT_AUTOVACUUM macro.*/int sqlite3BtreeSetAutoVacuum(Btree *p, int autoVacuum){#ifdef SQLITE_OMIT_AUTOVACUUM return SQLITE_READONLY;#else BtShared *pBt = p->pBt; int rc = SQLITE_OK; int av = (autoVacuum?1:0); sqlite3BtreeEnter(p); if( pBt->pageSizeFixed && av!=pBt->autoVacuum ){ rc = SQLITE_READONLY; }else{ pBt->autoVacuum = av; } sqlite3BtreeLeave(p); return rc;#endif}/*** Return the value of the 'auto-vacuum' property. If auto-vacuum is ** enabled 1 is returned. Otherwise 0.*/int sqlite3BtreeGetAutoVacuum(Btree *p){#ifdef SQLITE_OMIT_AUTOVACUUM return BTREE_AUTOVACUUM_NONE;#else int rc; sqlite3BtreeEnter(p); rc = ( (!p->pBt->autoVacuum)?BTREE_AUTOVACUUM_NONE: (!p->pBt->incrVacuum)?BTREE_AUTOVACUUM_FULL: BTREE_AUTOVACUUM_INCR ); sqlite3BtreeLeave(p); return rc;#endif}/*** Get a reference to pPage1 of the database file. This will** also acquire a readlock on that file.**** SQLITE_OK is returned on success. If the file is not a** well-formed database file, then SQLITE_CORRUPT is returned.** SQLITE_BUSY is returned if the database is locked. SQLITE_NOMEM** is returned if we run out of memory. */static int lockBtree(BtShared *pBt){ int rc; MemPage *pPage1; int nPage; assert( sqlite3_mutex_held(pBt->mutex) ); if( pBt->pPage1 ) return SQLITE_OK; rc = sqlite3BtreeGetPage(pBt, 1, &pPage1, 0); if( rc!=SQLITE_OK ) return rc; /* Do some checking to help insure the file we opened really is ** a valid database file. */ rc = sqlite3PagerPagecount(pBt->pPager, &nPage); if( rc!=SQLITE_OK ){ goto page1_init_failed; }else if( nPage>0 ){ int pageSize; int usableSize; u8 *page1 = pPage1->aData; rc = SQLITE_NOTADB; if( memcmp(page1, zMagicHeader, 16)!=0 ){ goto page1_init_failed; } if( page1[18]>1 ){ pBt->readOnly = 1; } if( page1[19]>1 ){ goto page1_init_failed; } /* The maximum embedded fraction must be exactly 25%. And the minimum ** embedded fraction must be 12.5% for both leaf-data and non-leaf-data. ** The original design allowed these amounts to vary, but as of ** version 3.6.0, we require them to be fixed. */ if( memcmp(&page1[21], "\100\040\040",3)!=0 ){ goto page1_init_failed; } pageSize = get2byte(&page1[16]); if( ((pageSize-1)&pageSize)!=0 || pageSize<512 || (SQLITE_MAX_PAGE_SIZE<32768 && pageSize>SQLITE_MAX_PAGE_SIZE) ){ goto page1_init_failed; } assert( (pageSize & 7)==0 ); usableSize = pageSize - page1[20]; if( pageSize!=pBt->pageSize ){ /* After reading the first page of the database assuming a page size ** of BtShared.pageSize, we have discovered that the page-size is ** actually pageSize. Unlock the database, leave pBt->pPage1 at ** zero and return SQLITE_OK. The caller will call this function ** again with the correct page-size. */ releasePage(pPage1); pBt->usableSize = usableSize; pBt->pageSize = pageSize; freeTempSpace(pBt); sqlite3PagerSetPagesize(pBt->pPager, &pBt->pageSize); return SQLITE_OK; } if( usableSize<500 ){ goto page1_init_failed; } pBt->pageSize = pageSize; pBt->usableSize = usableSize;#ifndef SQLITE_OMIT_AUTOVACUUM pBt->autoVacuum = (get4byte(&page1[36 + 4*4])?1:0); pBt->incrVacuum = (get4byte(&page1[36 + 7*4])?1:0);#endif } /* maxLocal is the maximum amount of payload to store locally for ** a cell. Make sure it is small enough so that at least minFanout ** cells can will fit on one page. We assume a 10-byte page header. ** Besides the payload, the cell must store: ** 2-byte pointer to the cell ** 4-byte child pointer ** 9-byte nKey value ** 4-byte nData value ** 4-byte overflow page pointer ** So a cell consists of a 2-byte poiner, a header which is as much as ** 17 bytes long, 0 to N bytes of payload, and an optional 4 byte overflow ** page pointer. */ pBt->maxLocal = (pBt->usableSize-12)*64/255 - 23; pBt->minLocal = (pBt->usableSize-12)*32/255 - 23; pBt->maxLeaf = pBt->usableSize - 35; pBt->minLeaf = (pBt->usableSize-12)*32/255 - 23; assert( pBt->maxLeaf + 23 <= MX_CELL_SIZE(pBt) ); pBt->pPage1 = pPage1; return SQLITE_OK;page1_init_failed: releasePage(pPage1); pBt->pPage1 = 0; return rc;}/*** This routine works like lockBtree() except that it also invokes the** busy callback if there is lock contention.*/static int lockBtreeWithRetry(Btree *pRef){ int rc = SQLITE_OK; assert( sqlite3BtreeHoldsMutex(pRef) ); if( pRef->inTrans==TRANS_NONE ){ u8 inTransaction = pRef->pBt->inTransaction; btreeIntegrity(pRef); rc = sqlite3BtreeBeginTrans(pRef, 0); pRef->pBt->inTransaction = inTransaction; pRef->inTrans = TRANS_NONE; if( rc==SQLITE_OK ){ pRef->pBt->nTransaction--; } btreeIntegrity(pRef); } return rc;} /*** If there are no outstanding cursors and we are not in the middle** of a transaction but there is a read lock on the database, then** this routine unrefs the first page of the database file which ** has the effect of releasing the read lock.**** If there are any outstanding cursors, this routine is a no-op.**** If there is a transaction in progress, this routine is a no-op.*/static void unlockBtreeIfUnused(BtShared *pBt){ assert( sqlite3_mutex_held(pBt->mutex) ); if( pBt->inTransaction==TRANS_NONE && pBt->pCursor==0 && pBt->pPage1!=0 ){ if( sqlite3PagerRefcount(pBt->pPager)>=1 ){ assert( pBt->pPage1->aData );#if 0 if( pBt->pPage1->aData==0 ){ MemPage *pPage = pBt->pPage1; pPage->aData = sqlite3PagerGetData(pPage->pDbPage); pPage->pBt = pBt; pPage->pgno = 1; }#endif releasePage(pBt->pPage1); } pBt->pPage1 = 0; pBt->inStmt = 0; }}/*** Create a new database by initializing the first page of the** file.*/static int newDatabase(BtShared *pBt){ MemPage *pP1; unsigned char *data; int rc; int nPage; assert( sqlite3_mutex_held(pBt->mutex) ); rc = sqlite3PagerPagecount(pBt->pPager, &nPage); if( rc!=SQLITE_OK || nPage>0 ){ return rc; } pP1 = pBt->pPage1; assert( pP1!=0 ); data = pP1->aData; rc = sqlite3PagerWrite(pP1->pDbPage); if( rc ) return rc; memcpy(data, zMagicHeader, sizeof(zMagicHeader)); assert( sizeof(zMagicHeader)==16 ); put2byte(&data[16], pBt->pageSize); data[18] = 1; data[19] = 1; data[20] = pBt->pageSize - pBt->usableSize; data[21] = 64; data[22] = 32; data[23] = 32; memset(&data[24], 0, 100-24); zeroPage(pP1, PTF_INTKEY|PTF_LEAF|PTF_LEAFDATA ); pBt->pageSizeFixed = 1;#ifndef SQLITE_OMIT_AUTOVACUUM assert( pBt->autoVacuum==1 || pBt->autoVacuum==0 ); assert( pBt->incrVacuum==1 || pBt->incrVacuum==0 ); put4byte(&data[36 + 4*4], pBt->autoVacuum); put4byte(&data[36 + 7*4], pBt->incrVacuum);#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** upgraded 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 an initial attempt to acquire the lock fails because of lock contention** and the database was previously unlocked, then invoke the busy handler** if there is one. But if there was previously a read-lock, do not** invoke the busy handler - just return SQLITE_BUSY. SQLITE_BUSY is ** returned when there is already a read-lock in order to avoid a deadlock.**** Suppose there are two processes A and B. A has a read lock and B has** a reserved lock. B tries to promote to exclusive but is blocked because** of A's read lock. A tries to promote to reserved but is blocked by B.** One or the other of the two processes must give way or there can be** no progress. By returning SQLITE_BUSY and not invoking the busy callback** when A already has a read lock, we encourage A to give up and let B** proceed.*/int sqlite3BtreeBeginTrans(Btree *p, int wrflag){ BtShared *pBt = p->pBt; int rc = SQLITE_OK; sqlite3BtreeEnter(p); pBt->db = p->db; btreeIntegrity(p); /* 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( p->inTrans==TRANS_WRITE || (p->inTrans==TRANS_READ && !wrflag) ){ goto trans_begun; } /* Write transactions are not possible on a read-only database */ if( pBt->readOnly && wrflag ){ rc = SQLITE_READONLY; goto trans_begun; } /* If another database handle has already opened a write transaction ** on this shared-btree structure and a second write transaction is ** requested, return SQLITE_BUSY. */ if( pBt->inTransaction==TRANS_WRITE && wrflag ){ rc = SQLITE_BUSY; goto trans_begun; }#ifndef SQLITE_OMIT_SHARED_CACHE if( wrflag>1 ){ BtLock *pIter; for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){ if( pIter->pBtree!=p ){ rc = SQLITE_BUSY; goto trans_begun; } } }#endif do { if( pBt->pPage1==0 ){ do{ rc = lockBtree(pBt); }while( pBt->pPage1==0 && rc==SQLITE_OK ); } if( rc==SQLITE_OK && wrflag ){ if( pBt->readOnly ){ rc = SQLITE_READONLY; }else{ rc = sqlite3PagerBegin(pBt->pPage1->pDbPage, wrflag>1); if( rc==SQLITE_OK ){ rc = newDatabase(pBt); } } } if( rc==SQLITE_OK ){ if( wrflag ) pBt->inStmt = 0; }else{ unlockBtreeIfUnused(pBt); } }while( rc==SQLITE_BUSY && pBt->inTransaction==TRANS_NONE && sqlite3BtreeInvokeBusyHandler(pBt, 0) ); if( rc==SQLITE_OK ){ if( p->inTrans==TRANS_NONE ){ pBt->nTransaction++; } p->inTrans = (wrflag?TRANS_WRITE:TRANS_READ); if( p->inTrans>pBt->inTransaction ){ pBt->inTransaction = p->inTrans; }#ifndef SQLITE_OMIT_SHARED_CACHE if( wrflag>1 ){ assert( !pBt->pExclusive ); pBt->pExclusive = p; }#endif }trans_begun: btreeIntegrity(p); sqlite3BtreeLeave(p); 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; /* Return code */ BtShared *pBt = pPage->pBt; int isInitOrig = pPage->isInit; Pgno pgno = pPage->pgno; assert( sqlite3_mutex_held(pPage->pBt->mutex) ); rc = sqlite3BtreeInitPage(pPage, pPage->pParent); if( rc!=SQLITE_OK ){ goto set_child_ptrmaps_out; } 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 g
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -