📄 btree.c
字号:
/*** 2004 April 6**** The author disclaims copyright to this source code. In place of** a legal notice, here is a blessing:**** May you do good and not evil.** May you find forgiveness for yourself and forgive others.** May you share freely, never taking more than you give.***************************************************************************** $Id: btree.c,v 1.565 2009/02/04 01:49:30 shane Exp $**** This file implements a external (disk-based) database using BTrees.** See the header comment on "btreeInt.h" for additional information.** Including a description of file format and an overview of operation.*/#include "btreeInt.h"/*** The header string that appears at the beginning of every** SQLite database.*/static const char zMagicHeader[] = SQLITE_FILE_HEADER;/*** Set this global variable to 1 to enable tracing using the TRACE** macro.*/#if 0int sqlite3BtreeTrace=0; /* True to enable tracing */# define TRACE(X) if(sqlite3BtreeTrace){printf X;fflush(stdout);}#else# define TRACE(X)#endif#ifndef SQLITE_OMIT_SHARED_CACHE/*** A list of BtShared objects that are eligible for participation** in shared cache. This variable has file scope during normal builds,** but the test harness needs to access it so we make it global for ** test builds.*/#ifdef SQLITE_TESTBtShared *SQLITE_WSD sqlite3SharedCacheList = 0;#elsestatic BtShared *SQLITE_WSD sqlite3SharedCacheList = 0;#endif#endif /* SQLITE_OMIT_SHARED_CACHE */#ifndef SQLITE_OMIT_SHARED_CACHE/*** Enable or disable the shared pager and schema features.**** This routine has no effect on existing database connections.** The shared cache setting effects only future calls to** sqlite3_open(), sqlite3_open16(), or sqlite3_open_v2().*/int sqlite3_enable_shared_cache(int enable){ sqlite3GlobalConfig.sharedCacheEnabled = enable; return SQLITE_OK;}#endif/*** Forward declaration*/static int checkReadLocks(Btree*, Pgno, BtCursor*, i64);#ifdef SQLITE_OMIT_SHARED_CACHE /* ** The functions queryTableLock(), lockTable() and unlockAllTables() ** manipulate entries in the BtShared.pLock linked list used to store ** shared-cache table level locks. If the library is compiled with the ** shared-cache feature disabled, then there is only ever one user ** of each BtShared structure and so this locking is not necessary. ** So define the lock related functions as no-ops. */ #define queryTableLock(a,b,c) SQLITE_OK #define lockTable(a,b,c) SQLITE_OK #define unlockAllTables(a)#endif#ifndef SQLITE_OMIT_SHARED_CACHE/*** Query to see if btree handle p may obtain a lock of type eLock ** (READ_LOCK or WRITE_LOCK) on the table with root-page iTab. Return** SQLITE_OK if the lock may be obtained (by calling lockTable()), or** SQLITE_LOCKED if not.*/static int queryTableLock(Btree *p, Pgno iTab, u8 eLock){ BtShared *pBt = p->pBt; BtLock *pIter; assert( sqlite3BtreeHoldsMutex(p) ); assert( eLock==READ_LOCK || eLock==WRITE_LOCK ); assert( p->db!=0 ); /* This is a no-op if the shared-cache is not enabled */ if( !p->sharable ){ return SQLITE_OK; } /* If some other connection is holding an exclusive lock, the ** requested lock may not be obtained. */ if( pBt->pExclusive && pBt->pExclusive!=p ){ return SQLITE_LOCKED; } /* This (along with lockTable()) is where the ReadUncommitted flag is ** dealt with. If the caller is querying for a read-lock and the flag is ** set, it is unconditionally granted - even if there are write-locks ** on the table. If a write-lock is requested, the ReadUncommitted flag ** is not considered. ** ** In function lockTable(), if a read-lock is demanded and the ** ReadUncommitted flag is set, no entry is added to the locks list ** (BtShared.pLock). ** ** To summarize: If the ReadUncommitted flag is set, then read cursors do ** not create or respect table locks. The locking procedure for a ** write-cursor does not change. */ if( 0==(p->db->flags&SQLITE_ReadUncommitted) || eLock==WRITE_LOCK || iTab==MASTER_ROOT ){ for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){ if( pIter->pBtree!=p && pIter->iTable==iTab && (pIter->eLock!=eLock || eLock!=READ_LOCK) ){ return SQLITE_LOCKED; } } } return SQLITE_OK;}#endif /* !SQLITE_OMIT_SHARED_CACHE */#ifndef SQLITE_OMIT_SHARED_CACHE/*** Add a lock on the table with root-page iTable to the shared-btree used** by Btree handle p. Parameter eLock must be either READ_LOCK or ** WRITE_LOCK.**** SQLITE_OK is returned if the lock is added successfully. SQLITE_BUSY and** SQLITE_NOMEM may also be returned.*/static int lockTable(Btree *p, Pgno iTable, u8 eLock){ BtShared *pBt = p->pBt; BtLock *pLock = 0; BtLock *pIter; assert( sqlite3BtreeHoldsMutex(p) ); assert( eLock==READ_LOCK || eLock==WRITE_LOCK ); assert( p->db!=0 ); /* This is a no-op if the shared-cache is not enabled */ if( !p->sharable ){ return SQLITE_OK; } assert( SQLITE_OK==queryTableLock(p, iTable, eLock) ); /* If the read-uncommitted flag is set and a read-lock is requested, ** return early without adding an entry to the BtShared.pLock list. See ** comment in function queryTableLock() for more info on handling ** the ReadUncommitted flag. */ if( (p->db->flags&SQLITE_ReadUncommitted) && (eLock==READ_LOCK) && iTable!=MASTER_ROOT ){ return SQLITE_OK; } /* First search the list for an existing lock on this table. */ for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){ if( pIter->iTable==iTable && pIter->pBtree==p ){ pLock = pIter; break; } } /* If the above search did not find a BtLock struct associating Btree p ** with table iTable, allocate one and link it into the list. */ if( !pLock ){ pLock = (BtLock *)sqlite3MallocZero(sizeof(BtLock)); if( !pLock ){ return SQLITE_NOMEM; } pLock->iTable = iTable; pLock->pBtree = p; pLock->pNext = pBt->pLock; pBt->pLock = pLock; } /* Set the BtLock.eLock variable to the maximum of the current lock ** and the requested lock. This means if a write-lock was already held ** and a read-lock requested, we don't incorrectly downgrade the lock. */ assert( WRITE_LOCK>READ_LOCK ); if( eLock>pLock->eLock ){ pLock->eLock = eLock; } return SQLITE_OK;}#endif /* !SQLITE_OMIT_SHARED_CACHE */#ifndef SQLITE_OMIT_SHARED_CACHE/*** Release all the table locks (locks obtained via calls to the lockTable()** procedure) held by Btree handle p.*/static void unlockAllTables(Btree *p){ BtShared *pBt = p->pBt; BtLock **ppIter = &pBt->pLock; assert( sqlite3BtreeHoldsMutex(p) ); assert( p->sharable || 0==*ppIter ); while( *ppIter ){ BtLock *pLock = *ppIter; assert( pBt->pExclusive==0 || pBt->pExclusive==pLock->pBtree ); if( pLock->pBtree==p ){ *ppIter = pLock->pNext; sqlite3_free(pLock); }else{ ppIter = &pLock->pNext; } } if( pBt->pExclusive==p ){ pBt->pExclusive = 0; }}#endif /* SQLITE_OMIT_SHARED_CACHE */static void releasePage(MemPage *pPage); /* Forward reference *//*** Verify that the cursor holds a mutex on the BtShared*/#ifndef NDEBUGstatic int cursorHoldsMutex(BtCursor *p){ return sqlite3_mutex_held(p->pBt->mutex);}#endif#ifndef SQLITE_OMIT_INCRBLOB/*** Invalidate the overflow page-list cache for cursor pCur, if any.*/static void invalidateOverflowCache(BtCursor *pCur){ assert( cursorHoldsMutex(pCur) ); sqlite3_free(pCur->aOverflow); pCur->aOverflow = 0;}/*** Invalidate the overflow page-list cache for all cursors opened** on the shared btree structure pBt.*/static void invalidateAllOverflowCache(BtShared *pBt){ BtCursor *p; assert( sqlite3_mutex_held(pBt->mutex) ); for(p=pBt->pCursor; p; p=p->pNext){ invalidateOverflowCache(p); }}#else #define invalidateOverflowCache(x) #define invalidateAllOverflowCache(x)#endif/*** Set bit pgno of the BtShared.pHasContent bitvec. This is called ** when a page that previously contained data becomes a free-list leaf ** page.**** The BtShared.pHasContent bitvec exists to work around an obscure** bug caused by the interaction of two useful IO optimizations surrounding** free-list leaf pages:**** 1) When all data is deleted from a page and the page becomes** a free-list leaf page, the page is not written to the database** (as free-list leaf pages contain no meaningful data). Sometimes** such a page is not even journalled (as it will not be modified,** why bother journalling it?).**** 2) When a free-list leaf page is reused, its content is not read** from the database or written to the journal file (why should it** be, if it is not at all meaningful?).**** By themselves, these optimizations work fine and provide a handy** performance boost to bulk delete or insert operations. However, if** a page is moved to the free-list and then reused within the same** transaction, a problem comes up. If the page is not journalled when** it is moved to the free-list and it is also not journalled when it** is extracted from the free-list and reused, then the original data** may be lost. In the event of a rollback, it may not be possible** to restore the database to its original configuration.**** The solution is the BtShared.pHasContent bitvec. Whenever a page is ** moved to become a free-list leaf page, the corresponding bit is** set in the bitvec. Whenever a leaf page is extracted from the free-list,** optimization 2 above is ommitted if the corresponding bit is already** set in BtShared.pHasContent. The contents of the bitvec are cleared** at the end of every transaction.*/static int btreeSetHasContent(BtShared *pBt, Pgno pgno){ int rc = SQLITE_OK; if( !pBt->pHasContent ){ int nPage; rc = sqlite3PagerPagecount(pBt->pPager, &nPage); if( rc==SQLITE_OK ){ pBt->pHasContent = sqlite3BitvecCreate((u32)nPage); if( !pBt->pHasContent ){ rc = SQLITE_NOMEM; } } } if( rc==SQLITE_OK && pgno<=sqlite3BitvecSize(pBt->pHasContent) ){ rc = sqlite3BitvecSet(pBt->pHasContent, pgno); } return rc;}/*** Query the BtShared.pHasContent vector.**** This function is called when a free-list leaf page is removed from the** free-list for reuse. It returns false if it is safe to retrieve the** page from the pager layer with the 'no-content' flag set. True otherwise.*/static int btreeGetHasContent(BtShared *pBt, Pgno pgno){ Bitvec *p = pBt->pHasContent; return (p && (pgno>sqlite3BitvecSize(p) || sqlite3BitvecTest(p, pgno)));}/*** Clear (destroy) the BtShared.pHasContent bitvec. This should be** invoked at the conclusion of each write-transaction.*/static void btreeClearHasContent(BtShared *pBt){ sqlite3BitvecDestroy(pBt->pHasContent); pBt->pHasContent = 0;}/*** Save the current cursor position in the variables BtCursor.nKey ** and BtCursor.pKey. The cursor's state is set to CURSOR_REQUIRESEEK.*/static int saveCursorPosition(BtCursor *pCur){ int rc; assert( CURSOR_VALID==pCur->eState ); assert( 0==pCur->pKey ); assert( cursorHoldsMutex(pCur) ); rc = sqlite3BtreeKeySize(pCur, &pCur->nKey); /* If this is an intKey table, then the above call to BtreeKeySize() ** stores the integer key in pCur->nKey. In this case this value is ** all that is required. Otherwise, if pCur is not open on an intKey ** table, then malloc space for and store the pCur->nKey bytes of key ** data. */ if( rc==SQLITE_OK && 0==pCur->apPage[0]->intKey){ void *pKey = sqlite3Malloc( (int)pCur->nKey ); if( pKey ){ rc = sqlite3BtreeKey(pCur, 0, (int)pCur->nKey, pKey); if( rc==SQLITE_OK ){ pCur->pKey = pKey; }else{ sqlite3_free(pKey); } }else{ rc = SQLITE_NOMEM; } } assert( !pCur->apPage[0]->intKey || !pCur->pKey ); if( rc==SQLITE_OK ){ int i; for(i=0; i<=pCur->iPage; i++){ releasePage(pCur->apPage[i]); pCur->apPage[i] = 0; } pCur->iPage = -1; pCur->eState = CURSOR_REQUIRESEEK; } invalidateOverflowCache(pCur); return rc;}/*** Save the positions of all cursors except pExcept open on the table ** with root-page iRoot. Usually, this is called just before cursor** pExcept is used to modify the table (BtreeDelete() or BtreeInsert()).*/static int saveAllCursors(BtShared *pBt, Pgno iRoot, BtCursor *pExcept){ BtCursor *p; assert( sqlite3_mutex_held(pBt->mutex) ); assert( pExcept==0 || pExcept->pBt==pBt ); for(p=pBt->pCursor; p; p=p->pNext){ if( p!=pExcept && (0==iRoot || p->pgnoRoot==iRoot) && p->eState==CURSOR_VALID ){ int rc = saveCursorPosition(p); if( SQLITE_OK!=rc ){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -