📄 btree.c
字号:
assert( pPage->isInit==0
|| pPage->nFree==nFree+data[hdr+7]+cellLimit-(cellOffset+2*nCell) );
cellOffset = pPage->cellOffset;
for(i=0; i<nCell; i++){
int size;
pc = get2byte(&data[cellOffset+2*i]);
assert( pc>0 && pc<usableSize-4 );
size = cellSize(pPage, &data[pc]);
assert( pc+size<=usableSize );
for(j=pc; j<pc+size; j++){
assert( used[j]==0 );
used[j] = 1;
}
}
for(i=cellOffset+2*nCell; i<cellimit; i++){
assert( used[i]==0 );
used[i] = 1;
}
nFree = 0;
for(i=0; i<usableSize; i++){
assert( used[i]<=1 );
if( used[i]==0 ) nFree++;
}
assert( nFree==data[hdr+7] );
sqliteFree(used);
}
#define pageIntegrity(X) _pageIntegrity(X)
#else
# define pageIntegrity(X)
#endif
/* A bunch of assert() statements to check the transaction state variables
** of handle p (type Btree*) are internally consistent.
*/
#define btreeIntegrity(p) \
assert( p->inTrans!=TRANS_NONE || p->pBt->nTransaction<p->pBt->nRef ); \
assert( p->pBt->nTransaction<=p->pBt->nRef ); \
assert( p->pBt->inTransaction!=TRANS_NONE || p->pBt->nTransaction==0 ); \
assert( p->pBt->inTransaction>=p->inTrans );
/*
** Defragment the page given. All Cells are moved to the
** end of the page and all free space is collected into one
** big FreeBlk that occurs in between the header and cell
** pointer array and the cell content area.
*/
static int defragmentPage(MemPage *pPage){
int i; /* Loop counter */
int pc; /* Address of a i-th cell */
int addr; /* Offset of first byte after cell pointer array */
int hdr; /* Offset to the page header */
int size; /* Size of a cell */
int usableSize; /* Number of usable bytes on a page */
int cellOffset; /* Offset to the cell pointer array */
int brk; /* Offset to the cell content area */
int nCell; /* Number of cells on the page */
unsigned char *data; /* The page data */
unsigned char *temp; /* Temp area for cell content */
assert( sqlite3pager_iswriteable(pPage->aData) );
assert( pPage->pBt!=0 );
assert( pPage->pBt->usableSize <= SQLITE_MAX_PAGE_SIZE );
assert( pPage->nOverflow==0 );
temp = sqliteMalloc( pPage->pBt->pageSize );
if( temp==0 ) return SQLITE_NOMEM;
data = pPage->aData;
hdr = pPage->hdrOffset;
cellOffset = pPage->cellOffset;
nCell = pPage->nCell;
assert( nCell==get2byte(&data[hdr+3]) );
usableSize = pPage->pBt->usableSize;
brk = get2byte(&data[hdr+5]);
memcpy(&temp[brk], &data[brk], usableSize - brk);
brk = usableSize;
for(i=0; i<nCell; i++){
u8 *pAddr; /* The i-th cell pointer */
pAddr = &data[cellOffset + i*2];
pc = get2byte(pAddr);
assert( pc<pPage->pBt->usableSize );
size = cellSizePtr(pPage, &temp[pc]);
brk -= size;
memcpy(&data[brk], &temp[pc], size);
put2byte(pAddr, brk);
}
assert( brk>=cellOffset+2*nCell );
put2byte(&data[hdr+5], brk);
data[hdr+1] = 0;
data[hdr+2] = 0;
data[hdr+7] = 0;
addr = cellOffset+2*nCell;
memset(&data[addr], 0, brk-addr);
sqliteFree(temp);
return SQLITE_OK;
}
/*
** Allocate nByte bytes of space on a page.
**
** Return the index into pPage->aData[] of the first byte of
** the new allocation. Or return 0 if there is not enough free
** space on the page to satisfy the allocation request.
**
** If the page contains nBytes of free space but does not contain
** nBytes of contiguous free space, then this routine automatically
** calls defragementPage() to consolidate all free space before
** allocating the new chunk.
*/
static int allocateSpace(MemPage *pPage, int nByte){
int addr, pc, hdr;
int size;
int nFrag;
int top;
int nCell;
int cellOffset;
unsigned char *data;
data = pPage->aData;
assert( sqlite3pager_iswriteable(data) );
assert( pPage->pBt );
if( nByte<4 ) nByte = 4;
if( pPage->nFree<nByte || pPage->nOverflow>0 ) return 0;
pPage->nFree -= nByte;
hdr = pPage->hdrOffset;
nFrag = data[hdr+7];
if( nFrag<60 ){
/* Search the freelist looking for a slot big enough to satisfy the
** space request. */
addr = hdr+1;
while( (pc = get2byte(&data[addr]))>0 ){
size = get2byte(&data[pc+2]);
if( size>=nByte ){
if( size<nByte+4 ){
memcpy(&data[addr], &data[pc], 2);
data[hdr+7] = nFrag + size - nByte;
return pc;
}else{
put2byte(&data[pc+2], size-nByte);
return pc + size - nByte;
}
}
addr = pc;
}
}
/* Allocate memory from the gap in between the cell pointer array
** and the cell content area.
*/
top = get2byte(&data[hdr+5]);
nCell = get2byte(&data[hdr+3]);
cellOffset = pPage->cellOffset;
if( nFrag>=60 || cellOffset + 2*nCell > top - nByte ){
if( defragmentPage(pPage) ) return 0;
top = get2byte(&data[hdr+5]);
}
top -= nByte;
assert( cellOffset + 2*nCell <= top );
put2byte(&data[hdr+5], top);
return top;
}
/*
** Return a section of the pPage->aData to the freelist.
** The first byte of the new free block is pPage->aDisk[start]
** and the size of the block is "size" bytes.
**
** Most of the effort here is involved in coalesing adjacent
** free blocks into a single big free block.
*/
static void freeSpace(MemPage *pPage, int start, int size){
int addr, pbegin, hdr;
unsigned char *data = pPage->aData;
assert( pPage->pBt!=0 );
assert( sqlite3pager_iswriteable(data) );
assert( start>=pPage->hdrOffset+6+(pPage->leaf?0:4) );
assert( (start + size)<=pPage->pBt->usableSize );
if( size<4 ) size = 4;
#ifdef SQLITE_SECURE_DELETE
/* Overwrite deleted information with zeros when the SECURE_DELETE
** option is enabled at compile-time */
memset(&data[start], 0, size);
#endif
/* Add the space back into the linked list of freeblocks */
hdr = pPage->hdrOffset;
addr = hdr + 1;
while( (pbegin = get2byte(&data[addr]))<start && pbegin>0 ){
assert( pbegin<=pPage->pBt->usableSize-4 );
assert( pbegin>addr );
addr = pbegin;
}
assert( pbegin<=pPage->pBt->usableSize-4 );
assert( pbegin>addr || pbegin==0 );
put2byte(&data[addr], start);
put2byte(&data[start], pbegin);
put2byte(&data[start+2], size);
pPage->nFree += size;
/* Coalesce adjacent free blocks */
addr = pPage->hdrOffset + 1;
while( (pbegin = get2byte(&data[addr]))>0 ){
int pnext, psize;
assert( pbegin>addr );
assert( pbegin<=pPage->pBt->usableSize-4 );
pnext = get2byte(&data[pbegin]);
psize = get2byte(&data[pbegin+2]);
if( pbegin + psize + 3 >= pnext && pnext>0 ){
int frag = pnext - (pbegin+psize);
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.
*/
static void decodeFlags(MemPage *pPage, int flagByte){
BtShared *pBt; /* A copy of pPage->pBt */
assert( pPage->hdrOffset==(pPage->pgno==1 ? 100 : 0) );
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.
*/
static int initPage(
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( pPage->pgno==sqlite3pager_pagenumber(pPage->aData) );
assert( pPage->aData == &((unsigned char*)pPage)[-pBt->pageSize] );
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;
sqlite3pager_ref(pParent->aData);
}
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;
pageIntegrity(pPage);
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( sqlite3pager_pagenumber(data)==pPage->pgno );
assert( &data[pBt->pageSize] == (unsigned char*)pPage );
assert( sqlite3pager_iswriteable(data) );
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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -