📄 btree.c
字号:
i64 nKey; /* The key for INTKEY tables, or number of bytes in key */
u32 nData; /* Number of bytes of data */
u16 nHeader; /* Size of the cell content header in bytes */
u16 nLocal; /* Amount of payload held locally */
u16 iOverflow; /* Offset to overflow page number. Zero if no overflow */
u16 nSize; /* Size of the cell content on the main b-tree page */
};
/*
** A cursor is a pointer to a particular entry in the BTree.
** The entry is identified by its MemPage and the index in
** MemPage.aCell[] of the entry.
*/
struct BtCursor {
Btree *pBtree; /* The Btree to which this cursor belongs */
BtCursor *pNext, *pPrev; /* Forms a linked list of all cursors */
int (*xCompare)(void*,int,const void*,int,const void*); /* Key comp func */
void *pArg; /* First arg to xCompare() */
Pgno pgnoRoot; /* The root page of this tree */
MemPage *pPage; /* Page that contains the entry */
int idx; /* Index of the entry in pPage->aCell[] */
CellInfo info; /* A parse of the cell we are pointing at */
u8 wrFlag; /* True if writable */
u8 eState; /* One of the CURSOR_XXX constants (see below) */
void *pKey; /* Saved key that was cursor's last known position */
i64 nKey; /* Size of pKey, or last integer key */
int skip; /* (skip<0) -> Prev() is a no-op. (skip>0) -> Next() is */
};
/*
** Potential values for BtCursor.eState.
**
** CURSOR_VALID:
** Cursor points to a valid entry. getPayload() etc. may be called.
**
** CURSOR_INVALID:
** Cursor does not point to a valid entry. This can happen (for example)
** because the table is empty or because BtreeCursorFirst() has not been
** called.
**
** CURSOR_REQUIRESEEK:
** The table that this cursor was opened on still exists, but has been
** modified since the cursor was last used. The cursor position is saved
** in variables BtCursor.pKey and BtCursor.nKey. When a cursor is in
** this state, restoreOrClearCursorPosition() can be called to attempt to
** seek the cursor to the saved position.
*/
#define CURSOR_INVALID 0
#define CURSOR_VALID 1
#define CURSOR_REQUIRESEEK 2
/*
** The TRACE macro will print high-level status information about the
** btree operation when the global variable sqlite3_btree_trace is
** enabled.
*/
#if SQLITE_TEST
# define TRACE(X) if( sqlite3_btree_trace )\
{ sqlite3DebugPrintf X; fflush(stdout); }
int sqlite3_btree_trace=0; /* True to enable tracing */
#else
# define TRACE(X)
#endif
/*
** Forward declaration
*/
static int checkReadLocks(Btree*,Pgno,BtCursor*);
/*
** Read or write a two- and four-byte big-endian integer values.
*/
static u32 get2byte(unsigned char *p){
return (p[0]<<8) | p[1];
}
static u32 get4byte(unsigned char *p){
return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3];
}
static void put2byte(unsigned char *p, u32 v){
p[0] = v>>8;
p[1] = v;
}
static void put4byte(unsigned char *p, u32 v){
p[0] = v>>24;
p[1] = v>>16;
p[2] = v>>8;
p[3] = v;
}
/*
** Routines to read and write variable-length integers. These used to
** be defined locally, but now we use the varint routines in the util.c
** file.
*/
#define getVarint sqlite3GetVarint
/* #define getVarint32 sqlite3GetVarint32 */
#define getVarint32(A,B) ((*B=*(A))<=0x7f?1:sqlite3GetVarint32(A,B))
#define putVarint sqlite3PutVarint
/* The database page the PENDING_BYTE occupies. This page is never used.
** TODO: This macro is very similary to PAGER_MJ_PGNO() in pager.c. They
** should possibly be consolidated (presumably in pager.h).
**
** If disk I/O is omitted (meaning that the database is stored purely
** in memory) then there is no pending byte.
*/
#ifdef SQLITE_OMIT_DISKIO
# define PENDING_BYTE_PAGE(pBt) 0x7fffffff
#else
# define PENDING_BYTE_PAGE(pBt) ((PENDING_BYTE/(pBt)->pageSize)+1)
#endif
/*
** A linked list of the following structures is stored at BtShared.pLock.
** Locks are added (or upgraded from READ_LOCK to WRITE_LOCK) when a cursor
** is opened on the table with root page BtShared.iTable. Locks are removed
** from this list when a transaction is committed or rolled back, or when
** a btree handle is closed.
*/
struct BtLock {
Btree *pBtree; /* Btree handle holding this lock */
Pgno iTable; /* Root page of table */
u8 eLock; /* READ_LOCK or WRITE_LOCK */
BtLock *pNext; /* Next in BtShared.pLock list */
};
/* Candidate values for BtLock.eLock */
#define READ_LOCK 1
#define WRITE_LOCK 2
#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)
#else
/*
** 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;
/* This is a no-op if the shared-cache is not enabled */
if( 0==sqlite3ThreadDataReadOnly()->useSharedData ){
return SQLITE_OK;
}
/* 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(
!p->pSqlite ||
0==(p->pSqlite->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;
}
/*
** 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;
/* This is a no-op if the shared-cache is not enabled */
if( 0==sqlite3ThreadDataReadOnly()->useSharedData ){
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->pSqlite) &&
(p->pSqlite->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 *)sqliteMalloc(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;
}
/*
** Release all the table locks (locks obtained via calls to the lockTable()
** procedure) held by Btree handle p.
*/
static void unlockAllTables(Btree *p){
BtLock **ppIter = &p->pBt->pLock;
/* If the shared-cache extension is not enabled, there should be no
** locks in the BtShared.pLock list, making this procedure a no-op. Assert
** that this is the case.
*/
assert( sqlite3ThreadDataReadOnly()->useSharedData || 0==*ppIter );
while( *ppIter ){
BtLock *pLock = *ppIter;
if( pLock->pBtree==p ){
*ppIter = pLock->pNext;
sqliteFree(pLock);
}else{
ppIter = &pLock->pNext;
}
}
}
#endif /* SQLITE_OMIT_SHARED_CACHE */
static void releasePage(MemPage *pPage); /* Forward reference */
/*
** 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 );
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->pPage->intKey){
void *pKey = sqliteMalloc(pCur->nKey);
if( pKey ){
rc = sqlite3BtreeKey(pCur, 0, pCur->nKey, pKey);
if( rc==SQLITE_OK ){
pCur->pKey = pKey;
}else{
sqliteFree(pKey);
}
}else{
rc = SQLITE_NOMEM;
}
}
assert( !pCur->pPage->intKey || !pCur->pKey );
if( rc==SQLITE_OK ){
releasePage(pCur->pPage);
pCur->pPage = 0;
pCur->eState = CURSOR_REQUIRESEEK;
}
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;
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 ){
return rc;
}
}
}
return SQLITE_OK;
}
/*
** Restore the cursor to the position it was in (or as close to as possible)
** when saveCursorPosition() was called. Note that this call deletes the
** saved position info stored by saveCursorPosition(), so there can be
** at most one effective restoreOrClearCursorPosition() call after each
** saveCursorPosition().
**
** If the second argument argument - doSeek - is false, then instead of
** returning the cursor to it's saved position, any saved position is deleted
** and the cursor state set to CURSOR_INVALID.
*/
static int restoreOrClearCursorPositionX(BtCursor *pCur, int doSeek){
int rc = SQLITE_OK;
assert( pCur->eState==CURSOR_REQUIRESEEK );
pCur->eState = CURSOR_INVALID;
if( doSeek ){
rc = sqlite3BtreeMoveto(pCur, pCur->pKey, pCur->nKey, &pCur->skip);
}
if( rc==SQLITE_OK ){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -