#include "sqliteInt.h"
#include "pager.h"
#include "btree.h"
#include <assert.h>
static BtOps sqliteBtreeOps;
static BtCursorOps sqliteBtreeCursorOps;
#define SWAB16(B,X) ((B)->needSwab? swab16((u16)X) : ((u16)X))
#define SWAB32(B,X) ((B)->needSwab? swab32(X) : (X))
#define SWAB_ADD(B,X,A) \
if((B)->needSwab){ X=swab32(swab32(X)+A); }else{ X += (A); }
#ifdef SQLITE_TEST
int btree_native_byte_order = 1;
#else
# define btree_native_byte_order 1
#endif
typedef struct PageOne PageOne;
typedef struct MemPage MemPage;
typedef struct PageHdr PageHdr;
typedef struct Cell Cell;
typedef struct CellHdr CellHdr;
typedef struct FreeBlk FreeBlk;
typedef struct OverflowPage OverflowPage;
typedef struct FreelistInfo FreelistInfo;
#define ROUNDUP(X) ((X+3) & ~3)
static const char zMagicHeader[] =
"** This file contains an SQLite 2.1 database **";
#define MAGIC_SIZE (sizeof(zMagicHeader))
#define MAGIC 0xdae37528
struct PageOne {
char zMagic[MAGIC_SIZE];
int iMagic;
Pgno freeList;
int nFree;
int aMeta[SQLITE_N_BTREE_META-1];
};
struct PageHdr {
Pgno rightChild;
u16 firstCell;
u16 firstFree;
};
struct CellHdr {
Pgno leftChild;
u16 nKey;
u16 iNext;
u8 nKeyHi;
u8 nDataHi;
u16 nData;
};
#define NKEY(b,h) (SWAB16(b,h.nKey) + h.nKeyHi*65536)
#define NDATA(b,h) (SWAB16(b,h.nData) + h.nDataHi*65536)
#define MIN_CELL_SIZE (sizeof(CellHdr)+4)
#define MX_CELL ((SQLITE_USABLE_SIZE-sizeof(PageHdr))/MIN_CELL_SIZE)
#define USABLE_SPACE (SQLITE_USABLE_SIZE - sizeof(PageHdr))
#define MX_LOCAL_PAYLOAD ((USABLE_SPACE/4-(sizeof(CellHdr)+sizeof(Pgno)))&~3)
struct Cell {
CellHdr h;
char aPayload[MX_LOCAL_PAYLOAD];
Pgno ovfl;
};
struct FreeBlk {
u16 iSize;
u16 iNext;
};
#define OVERFLOW_SIZE (SQLITE_USABLE_SIZE-sizeof(Pgno))
struct OverflowPage {
Pgno iNext;
char aPayload[OVERFLOW_SIZE];
};
struct FreelistInfo {
int nFree;
Pgno aFree[(OVERFLOW_SIZE-sizeof(int))/sizeof(Pgno)];
};
struct MemPage {
union u_page_data {
char aDisk[SQLITE_PAGE_SIZE];
PageHdr hdr;
} u;
u8 isInit;
u8 idxShift;
u8 isOverfull;
MemPage *pParent;
int idxParent;
int nFree;
int nCell;
Cell *apCell[MX_CELL+2];
};
#define EXTRA_SIZE (sizeof(MemPage)-sizeof(union u_page_data))
struct Btree {
BtOps *pOps;
Pager *pPager;
BtCursor *pCursor;
PageOne *page1;
u8 inTrans;
u8 inCkpt;
u8 readOnly;
u8 needSwab;
};
typedef Btree Bt;
struct BtCursor {
BtCursorOps *pOps;
Btree *pBt;
BtCursor *pNext, *pPrev;
BtCursor *pShared;
Pgno pgnoRoot;
MemPage *pPage;
int idx;
u8 wrFlag;
u8 eSkip;
u8 iMatch;
};
#define SKIP_NONE 0
#define SKIP_NEXT 1
#define SKIP_PREV 2
#define SKIP_INVALID 3
static int fileBtreeCloseCursor(BtCursor *pCur);
u16 swab16(u16 x){
return ((x & 0xff)<<8) | ((x>>8)&0xff);
}
u32 swab32(u32 x){
return ((x & 0xff)<<24) | ((x & 0xff00)<<8) |
((x>>8) & 0xff00) | ((x>>24)&0xff);
}
static int cellSize(Btree *pBt, Cell *pCell){
int n = NKEY(pBt, pCell->h) + NDATA(pBt, pCell->h);
if( n>MX_LOCAL_PAYLOAD ){
n = MX_LOCAL_PAYLOAD + sizeof(Pgno);
}else{
n = ROUNDUP(n);
}
n += sizeof(CellHdr);
return n;
}
static void defragmentPage(Btree *pBt, MemPage *pPage){
int pc, i, n;
FreeBlk *pFBlk;
char newPage[SQLITE_USABLE_SIZE];
assert( sqlitepager_iswriteable(pPage) );
assert( pPage->isInit );
pc = sizeof(PageHdr);
pPage->u.hdr.firstCell = SWAB16(pBt, pc);
memcpy(newPage, pPage->u.aDisk, pc);
for(i=0; i<pPage->nCell; i++){
Cell *pCell = pPage->apCell[i];
assert( Addr(pCell) > Addr(pPage) );
assert( Addr(pCell) < Addr(pPage) + SQLITE_USABLE_SIZE );
n = cellSize(pBt, pCell);
pCell->h.iNext = SWAB16(pBt, pc + n);
memcpy(&newPage[pc], pCell, n);
pPage->apCell[i] = (Cell*)&pPage->u.aDisk[pc];
pc += n;
}
assert( pPage->nFree==SQLITE_USABLE_SIZE-pc );
memcpy(pPage->u.aDisk, newPage, pc);
if( pPage->nCell>0 ){
pPage->apCell[pPage->nCell-1]->h.iNext = 0;
}
pFBlk = (FreeBlk*)&pPage->u.aDisk[pc];
pFBlk->iSize = SWAB16(pBt, SQLITE_USABLE_SIZE - pc);
pFBlk->iNext = 0;
pPage->u.hdr.firstFree = SWAB16(pBt, pc);
memset(&pFBlk[1], 0, SQLITE_USABLE_SIZE - pc - sizeof(FreeBlk));
}
static int allocateSpace(Btree *pBt, MemPage *pPage, int nByte){
FreeBlk *p;
u16 *pIdx;
int start;
int iSize;
#ifndef NDEBUG
int cnt = 0;
#endif
assert( sqlitepager_iswriteable(pPage) );
assert( nByte==ROUNDUP(nByte) );
assert( pPage->isInit );
if( pPage->nFree<nByte || pPage->isOverfull ) return 0;
pIdx = &pPage->u.hdr.firstFree;
p = (FreeBlk*)&pPage->u.aDisk[SWAB16(pBt, *pIdx)];
while( (iSize = SWAB16(pBt, p->iSize))<nByte ){
assert( cnt++ < SQLITE_USABLE_SIZE/4 );
if( p->iNext==0 ){
defragmentPage(pBt, pPage);
pIdx = &pPage->u.hdr.firstFree;
}else{
pIdx = &p->iNext;
}
p = (FreeBlk*)&pPage->u.aDisk[SWAB16(pBt, *pIdx)];
}
if( iSize==nByte ){
start = SWAB16(pBt, *pIdx);
*pIdx = p->iNext;
}else{
FreeBlk *pNew;
start = SWAB16(pBt, *pIdx);
pNew = (FreeBlk*)&pPage->u.aDisk[start + nByte];
pNew->iNext = p->iNext;
pNew->iSize = SWAB16(pBt, iSize - nByte);
*pIdx = SWAB16(pBt, start + nByte);
}
pPage->nFree -= nByte;
return start;
}
static void freeSpace(Btree *pBt, MemPage *pPage, int start, int size){
int end = start + size;
u16 *pIdx, idx;
FreeBlk *pFBlk;
FreeBlk *pNew;
FreeBlk *pNext;
int iSize;
assert( sqlitepager_iswriteable(pPage) );
assert( size == ROUNDUP(size) );
assert( start == ROUNDUP(start) );
assert( pPage->isInit );
pIdx = &pPage->u.hdr.firstFree;
idx = SWAB16(pBt, *pIdx);
while( idx!=0 && idx<start ){
pFBlk = (FreeBlk*)&pPage->u.aDisk[idx];
iSize = SWAB16(pBt, pFBlk->iSize);
if( idx + iSize == start ){
pFBlk->iSize = SWAB16(pBt, iSize + size);
if( idx + iSize + size == SWAB16(pBt, pFBlk->iNext) ){
pNext = (FreeBlk*)&pPage->u.aDisk[idx + iSize + size];
if( pBt->needSwab ){
pFBlk->iSize = swab16((u16)swab16(pNext->iSize)+iSize+size);
}else{
pFBlk->iSize += pNext->iSize;
}
pFBlk->iNext = pNext->iNext;
}
pPage->nFree += size;
return;
}
pIdx = &pFBlk->iNext;
idx = SWAB16(pBt, *pIdx);
}
pNew = (FreeBlk*)&pPage->u.aDisk[start];
if( idx != end ){
pNew->iSize = SWAB16(pBt, size);
pNew->iNext = SWAB16(pBt, idx);
}else{
pNext = (FreeBlk*)&pPage->u.aDisk[idx];
pNew->iSize = SWAB16(pBt, size + SWAB16(pBt, pNext->iSize));
pNew->iNext = pNext->iNext;
}
*pIdx = SWAB16(pBt, start);
pPage->nFree += size;
}
static int initPage(Bt *pBt, MemPage *pPage, Pgno pgnoThis, MemPage *pParent){
int idx;
Cell *pCell;
FreeBlk *pFBlk;
int sz;
int freeSpace;
if( pPage->pParent ){
assert( pPage->pParent==pParent );
return SQLITE_OK;
}
if( pParent ){
pPage->pParent = pParent;
sqlitepager_ref(pParent);
}
if( pPage->isInit ) return SQLITE_OK;
pPage->isInit = 1;
pPage->nCell = 0;
freeSpace = USABLE_SPACE;
idx = SWAB16(pBt, pPage->u.hdr.firstCell);
while( idx!=0 ){
if( idx>SQLITE_USABLE_SIZE-MIN_CELL_SIZE ) goto page_format_error;
if( idx<sizeof(PageHdr) ) goto page_format_error;
if( idx!=ROUNDUP(idx) ) goto page_format_error;
pCell = (Cell*)&pPage->u.aDisk[idx];
sz = cellSize(pBt, pCell);
if( idx+sz > SQLITE_USABLE_SIZE ) goto page_format_error;
freeSpace -= sz;
pPage->apCell[pPage->nCell++] = pCell;
idx = SWAB16(pBt, pCell->h.iNext);
}
pPage->nFree = 0;
idx = SWAB16(pBt, pPage->u.hdr.firstFree);
while( idx!=0 ){
int iNext;
if( idx>SQLITE_USABLE_SIZE-sizeof(FreeBlk) ) goto page_format_error;
if( idx<sizeof(PageHdr) ) goto page_format_error;
pFBlk = (FreeBlk*)&pPage->u.aDisk[idx];
pPage->nFree += SWAB16(pBt, pFBlk->iSize);
iNext = SWAB16(pBt, pFBlk->iNext);
if( iNext>0 && iNext <= idx ) goto page_format_error;
idx = iNext;
}
if( pPage->nCell==0 && pPage->nFree==0 ){
return SQLITE_OK;
}
if( pPage->nFree!=freeSpace ) goto page_format_error;
return SQLITE_OK;
page_format_error:
return SQLITE_CORRUPT;
}
static void zeroPage(Btree *pBt, MemPage *pPage){
PageHdr *pHdr;
FreeBlk *pFBlk;
assert( sqlitepager_iswriteable(pPage) );
memset(pPage, 0, SQLITE_USABLE_SIZE);
pHdr = &pPage->u.hdr;
pHdr->firstCell = 0;
pHdr->firstFree = SWAB16(pBt, sizeof(*pHdr));
pFBlk = (FreeBlk*)&pHdr[1];
pFBlk->iNext = 0;
pPage->nFree = SQLITE_USABLE_SIZE - sizeof(*pHdr);
pFBlk->iSize = SWAB16(pBt, pPage->nFree);
pPage->nCell = 0;
pPage->isOverfull = 0;
}
static void pageDestructor(void *pData){
MemPage *pPage = (MemPage*)pData;
if( pPage->pParent ){
MemPage *pParent = pPage->pParent;
pPage->pParent = 0;
sqlitepager_unref(pParent);
}
}
int sqliteBtreeOpen(
const char *zFilename,
int omitJournal,
int nCache,
Btree **ppBtree
){
Btree *pBt;
int rc;
assert( sizeof(u32)==4 );
assert( sizeof(u16)==2 );
assert( sizeof(Pgno)==4 );
assert( sizeof(PageHdr)==8 );
assert( sizeof(CellHdr)==12 );
assert( sizeof(FreeBlk)==4 );
assert( sizeof(OverflowPage)==SQLITE_USABLE_SIZE );
assert( sizeof(FreelistInfo)==OVERFLOW_SIZE );
assert( sizeof(ptr)==sizeof(char*) );
assert( sizeof(uptr)==sizeof(ptr) );
pBt = sqliteMalloc( sizeof(*pBt) );
if( pBt==0 ){
*ppBtree = 0;
return SQLITE_NOMEM;
}
if( nCache<10 ) nCache = 10;
rc = sqlitepager_open(&pBt->pPager, zFilename, nCache, EXTRA_SIZE,
!omitJournal);
if( rc!=SQLITE_OK ){
if( pBt->pPager ) sqlitepager_close(pBt->pPager);
sqliteFree(pBt);
*ppBtree = 0;
return rc;
}
sqlitepager_set_destructor(pBt->pPager, pageDestructor);
pBt->pCursor = 0;
pBt->page1 = 0;
pBt->readOnly = sqlitepager_isreadonly(pBt->pPager);
pBt->pOps = &sqliteBtreeOps;
*ppBtree = pBt;
return SQLITE_OK;
}
static int fileBtreeClose(Btree *pBt){
while( pBt->pCursor ){
fileBtreeCloseCursor(pBt->pCursor);
}
sqlitepager_close(pBt->pPager);
sqliteFree(pBt);
return SQLITE_OK;
}
static int fileBtreeSetCacheSize(Btree *pBt, int mxPage){
sqlitepager_set_cachesize(pBt->pPager, mxPage);
return SQLITE_OK;
}
static int fileBtreeSetSafetyLevel(Btree *pBt, int level){
sqlitepager_set_safety_level(pBt->pPager, level);
return SQLITE_OK;
}
static int lockBtree(Btree *pBt){
int rc;
if( pBt->page1 ) return SQLITE_OK;
rc = sqlitepager_get(pBt->pPager, 1, (void**)&pBt->page1);
if( rc!=SQLITE_OK ) return rc;
if( sqlitepager_pagecount(pBt->pPager)>0 ){
PageOne *pP1 = pBt->page1;
if( strcmp(pP1->zMagic,zMagicHeader)!=0 ||
(pP1->iMagic!=MAGIC && swab32(pP1->iMagic)!=MAGIC) ){
rc = SQLITE_NOTADB;
goto page1_init_failed;
}
pBt->needSwab = pP1->iMagic!=MAGIC;
}
return rc;
page1_init_failed:
sqlitepager_unref(pBt->page1);
pBt->page1 = 0;
return rc;
}
static void unlockBtreeIfUnused(Btree *pBt){
if( pBt->inTrans==0 && pBt->pCursor==0 && pBt->page1!=0 ){
sqlitepager_unref(pBt->page1);
pBt->page1 = 0;
pBt->inTrans = 0;
pBt->inCkpt = 0;
}
}
static int newDatabase(Btree *pBt){
MemPage *pRoot;
PageOne *pP1;
int rc;
if( sqlitepager_pagecount(pBt->pPager)>1 ) return SQLITE_OK;
pP1 = pBt->page1;
rc = sqlitepager_write(pBt->page1);
if( rc ) return rc;
rc = sqlitepager_get(pBt->pPager, 2, (void**)&pRoot);
if( rc ) return rc;
rc = sqlitepager_write(pRoot);
if( rc ){
sqlitepager_unref(pRoot);
return rc;
}
strcpy(pP1->zMagic, zMagicHeader);
if( btree_native_byte_order ){
pP1->iMagic = MAGIC;
pBt->needSwab = 0;
}else{
pP1->iMagic = swab32(MAGIC);
pBt->needSwab = 1;
}
zeroPage(pBt, pRoot);
sqlitepager_unref(pRoot);
return SQLITE_OK;
}
static int fileBtreeBeginTrans(Btree *pBt){
int rc;
if( pBt->inTrans ) return SQLITE_ERROR;
if( pBt->readOnly ) return SQLITE_READONLY;
if( pBt->page1==0 ){
rc = lockBtree(pBt);
if( rc!=SQLITE_OK ){
return rc;
}
}
rc = sqlitepager_begin(pBt->page1);
if( rc==SQLITE_OK ){
rc = newDatabase(pBt);
}
if( rc==SQLITE_OK ){
pBt->inTrans = 1;
pBt->inCkpt = 0;
}else{
unlockBtreeIfUnused(pBt);
}
return rc;
}
static int fileBtreeCommit(Btree *pBt){
int rc;
rc = pBt->readOnly ? SQLITE_OK : sqlitepager_commit(pBt->pPager);
pBt->inTrans = 0;
pBt->inCkpt = 0;
unlockBtreeIfUnused(pBt);
return rc;
}
static int fileBtreeRollback(Btree *pBt){
int rc;
BtCursor *pCur;
if( pBt->inTrans==0 ) return SQLITE_OK;
pBt->inTrans = 0;
pBt->inCkpt = 0;
rc = pBt->readOnly ? SQLITE_OK : sqlitepager_rollback(pBt->pPager);
for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
if( pCur->pPage && pCur->pPage->isInit==0 ){
sqlitepager_unref(pCur->pPage);
pCur->pPage = 0;
}
}
unlockBtreeIfUnused(pBt);
return rc;
}
static int fileBtreeBeginCkpt(Btree *pBt){
int rc;
if( !pBt->inTrans || pBt->inCkpt ){
return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
}
rc = pBt->readOnly ? SQLITE_OK : sqlitepager_ckpt_begin(pBt->pPager);
pBt->inCkpt = 1;
return rc;
}
static int fileBtreeCommitCkpt(Btree *pBt){
int rc;
if( pBt->inCkpt && !pBt->readOnly ){
rc = sqlitepager_ckpt_commit(pBt->pPager);
}else{
rc = SQLITE_OK;
}
pBt->inCkpt = 0;
return rc;
}
static int fileBtreeRollbackCkpt(Btree *pBt){
int rc;
BtCursor *pCur;
if( pBt->inCkpt==0 || pBt->readOnly ) return SQLITE_OK;
rc = sqlitepager_ckpt_rollback(pBt->pPager);
for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
if( pCur->pPage && pCur->pPage->isInit==0 ){
sqlitepager_unref(pCur->pPage);
pCur->pPage = 0;
}
}
pBt->inCkpt = 0;
return rc;
}
static
int fileBtreeCursor(Btree *pBt, int iTable, int wrFlag, BtCursor **ppCur){
int rc;
BtCursor *pCur, *pRing;
if( pBt->readOnly && wrFlag ){
*ppCur = 0;
return SQLITE_READONLY;
}
if( pBt->page1==0 ){
rc = lockBtree(pBt);
if( rc!=SQLITE_OK ){
*ppCur = 0;
return rc;
}
}
pCur = sqliteMalloc( sizeof(*pCur) );
if( pCur==0 ){
rc = SQLITE_NOMEM;
goto create_cursor_exception;
}
pCur->pgnoRoot = (Pgno)iTable;
rc = sqlitepager_get(pBt->pPager, pCur->pgnoRoot, (void**)&pCur->pPage);
if( rc!=SQLITE_OK ){
goto create_cursor_exception;
}
rc = initPage(pBt, pCur->pPage, pCur->pgnoRoot, 0);
if( rc!=SQLITE_OK ){
goto create_cursor_exception;
}
pCur->pOps = &sqliteBtreeCursorOps;
pCur->pBt = pBt;
pCur->wrFlag = wrFlag;
pCur->idx = 0;
pCur->eSkip = SKIP_INVALID;
pCur->pNext = pBt->pCursor;
if( pCur->pNext ){
pCur->pNext->pPrev = pCur;
}
pCur->pPrev = 0;
pRing = pBt->pCursor;
while( pRing && pRing->pgnoRoot!=pCur->pgnoRoot ){ pRing = pRing->pNext; }
if( pRing ){
pCur->pShared = pRing->pShared;
pRing->pShared = pCur;
}else{
pCur->pShared = pCur;
}
pBt->pCursor = pCur;
*ppCur = pCur;
return SQLITE_OK;
create_cursor_exception:
*ppCur = 0;
if( pCur ){
if( pCur->pPage ) sqlitepager_unref(pCur->pPage);
sqliteFree(pCur);
}
unlockBtreeIfUnused(pBt);
return rc;
}
static int fileBtreeCloseCursor(BtCursor *pCur){
Btree *pBt = pCur->pBt;
if( pCur->pPrev ){
pCur->pPrev->pNext = pCur->pNext;
}else{
pBt->pCursor = pCur->pNext;
}
if( pCur->pNext ){
pCur->pNext->pPrev = pCur->pPrev;
}
if( pCur->pPage ){
sqlitepager_unref(pCur->pPage);
}
if( pCur->pShared!=pCur ){
BtCursor *pRing = pCur->pShared;
while( pRing->pShared!=pCur ){ pRing = pRing->pShared; }
pRing->pShared = pCur->pShared;
}
unlockBtreeIfUnused(pBt);
sqliteFree(pCur);
return SQLITE_OK;
}
static void getTempCursor(BtCursor *pCur, BtCursor *pTempCur){
memcpy(pTempCur, pCur, sizeof(*pCur));
pTempCur->pNext = 0;
pTempCur->pPrev = 0;
if( pTempCur->pPage ){
sqlitepager_ref(pTempCur->pPage);
}
}
static void releaseTempCursor(BtCursor *pCur){
if( pCur->pPage ){
sqlitepager_unref(pCur->pPage);
}
}
static int fileBtreeKeySize(BtCursor *pCur, int *pSize){
Cell *pCell;
MemPage *pPage;
pPage = pCur->pPage;
assert( pPage!=0 );
if( pCur->idx >= pPage->nCell ){
*pSize = 0;
}else{
pCell = pPage->apCell[pCur->idx];
*pSize = NKEY(pCur->pBt, pCell->h);
}
return SQLITE_OK;
}
static int getPayload(BtCursor *pCur, int offset, int amt, char *zBuf){
char *aPayload;
Pgno nextPage;
int rc;
Btree *pBt = pCur->pBt;
assert( pCur!=0 && pCur->pPage!=0 );
assert( pCur->idx>=0 && pCur->idx<pCur->pPage->nCell );
aPayload = pCur->pPage->apCell[pCur->idx]->aPayload;
if( offset<MX_LOCAL_PAYLOAD ){
int a = amt;
if( a+offset>MX_LOCAL_PAYLOAD ){
a = MX_LOCAL_PAYLOAD - offset;
}
memcpy(zBuf, &aPayload[offset], a);
if( a==amt ){
return SQLITE_OK;
}
offset = 0;
zBuf += a;
amt -= a;
}else{
offset -= MX_LOCAL_PAYLOAD;
}
if( amt>0 ){
nextPage = SWAB32(pBt, pCur->pPage->apCell[pCur->idx]->ovfl);
}
while( amt>0 && nextPage ){
OverflowPage *pOvfl;
rc = sqlitepager_get(pBt->pPager, nextPage, (void**)&pOvfl);
if( rc!=0 ){
return rc;
}
nextPage = SWAB32(pBt, pOvfl->iNext);
if( offset<OVERFLOW_SIZE ){
int a = amt;
if( a + offset > OVERFLOW_SIZE ){
a = OVERFLOW_SIZE - offset;
}
memcpy(zBuf, &pOvfl->aPayload[offset], a);
offset = 0;
amt -= a;
zBuf += a;
}else{
offset -= OVERFLOW_SIZE;
}
sqlitepager_unref(pOvfl);
}
if( amt>0 ){
return SQLITE_CORRUPT;
}
return SQLITE_OK;
}
static int fileBtreeKey(BtCursor *pCur, int offset, int amt, char *zBuf){
MemPage *pPage;
assert( amt>=0 );
assert( offset>=0 );
assert( pCur->pPage!=0 );
pPage = pCur->pPage;
if( pCur->idx >= pPage->nCell ){
return 0;
}
assert( amt+offset <= NKEY(pCur->pBt, pPage->apCell[pCur->idx]->h) );
getPayload(pCur, offset, amt, zBuf);
return amt;
}
static int fileBtreeDataSize(BtCursor *pCur, int *pSize){
Cell *pCell;
MemPage *pPage;
pPage = pCur->pPage;
assert( pPage!=0 );
if( pCur->idx >= pPage->nCell ){
*pSize = 0;
}else{
pCell = pPage->apCell[pCur->idx];
*pSize = NDATA(pCur->pBt, pCell->h);
}
return SQLITE_OK;
}
static int fileBtreeData(BtCursor *pCur, int offset, int amt, char *zBuf){
Cell *pCell;
MemPage *pPage;
assert( amt>=0 );
assert( offset>=0 );
assert( pCur->pPage!=0 );
pPage = pCur->pPage;
if( pCur->idx >= pPage->nCell ){
return 0;
}
pCell = pPage->apCell[pCur->idx];
assert( amt+offset <= NDATA(pCur->pBt, pCell->h) );
getPayload(pCur, offset + NKEY(pCur->pBt, pCell->h), amt, zBuf);
return amt;
}
static int fileBtreeKeyCompare(
BtCursor *pCur,
const void *pKey,
int nKey,
int nIgnore,
int *pResult
){
Pgno nextPage;
int n, c, rc, nLocal;
Cell *pCell;
Btree *pBt = pCur->pBt;
const char *zKey = (const char*)pKey;
assert( pCur->pPage );
assert( pCur->idx>=0 && pCur->idx<pCur->pPage->nCell );
pCell = pCur->pPage->apCell[pCur->idx];
nLocal = NKEY(pBt, pCell->h) - nIgnore;
if( nLocal<0 ) nLocal = 0;
n = nKey<nLocal ? nKey : nLocal;
if( n>MX_LOCAL_PAYLOAD ){
n = MX_LOCAL_PAYLOAD;
}
c = memcmp(pCell->aPayload, zKey, n);
if( c!=0 ){
*pResult = c;
return SQLITE_OK;
}
zKey += n;
nKey -= n;
nLocal -= n;
nextPage = SWAB32(pBt, pCell->ovfl);
while( nKey>0 && nLocal>0 ){
OverflowPage *pOvfl;
if( nextPage==0 ){
return SQLITE_CORRUPT;
}
rc = sqlitepager_get(pBt->pPager, nextPage, (void**)&pOvfl);
if( rc ){
return rc;
}
nextPage = SWAB32(pBt, pOvfl->iNext);
n = nKey<nLocal ? nKey : nLocal;
if( n>OVERFLOW_SIZE ){
n = OVERFLOW_SIZE;
}
c = memcmp(pOvfl->aPayload, zKey, n);
sqlitepager_unref(pOvfl);
if( c!=0 ){
*pResult = c;
return SQLITE_OK;
}
nKey -= n;
nLocal -= n;
zKey += n;
}
if( c==0 ){
c = nLocal - nKey;
}
*pResult = c;
return SQLITE_OK;
}
static int moveToChild(BtCursor *pCur, int newPgno){
int rc;
MemPage *pNewPage;
Btree *pBt = pCur->pBt;
newPgno = SWAB32(pBt, newPgno);
rc = sqlitepager_get(pBt->pPager, newPgno, (void**)&pNewPage);
if( rc ) return rc;
rc = initPage(pBt, pNewPage, newPgno, pCur->pPage);
if( rc ) return rc;
assert( pCur->idx>=pCur->pPage->nCell
|| pCur->pPage->apCell[pCur->idx]->h.leftChild==SWAB32(pBt,newPgno) );
assert( pCur->idx<pCur->pPage->nCell
|| pCur->pPage->u.hdr.rightChild==SWAB32(pBt,newPgno) );
pNewPage->idxParent = pCur->idx;
pCur->pPage->idxShift = 0;
sqlitepager_unref(pCur->pPage);
pCur->pPage = pNewPage;
pCur->idx = 0;
if( pNewPage->nCell<1 ){
return SQLITE_CORRUPT;
}
return SQLITE_OK;
}
static void moveToParent(BtCursor *pCur){
Pgno oldPgno;
MemPage *pParent;
MemPage *pPage;
int idxParent;
pPage = pCur->pPage;
assert( pPage!=0 );
pParent = pPage->pParent;
assert( pParent!=0 );
idxParent = pPage->idxParent;
sqlitepager_ref(pParent);
sqlitepager_unref(pPage);
pCur->pPage = pParent;
assert( pParent->idxShift==0 );
if( pParent->idxShift==0 ){
pCur->idx = idxParent;
#ifndef NDEBUG
oldPgno = SWAB32(pCur->pBt, sqlitepager_pagenumber(pPage));
if( pCur->idx<pParent->nCell ){
assert( pParent->apCell[idxParent]->h.leftChild==oldPgno );
}else{
assert( pParent->u.hdr.rightChild==oldPgno );
}
#endif
}else{
int i;
pCur->idx = pParent->nCell;
oldPgno = SWAB32(pCur->pBt, sqlitepager_pagenumber(pPage));
for(i=0; i<pParent->nCell; i++){
if( pParent->apCell[i]->h.leftChild==oldPgno ){
pCur->idx = i;
break;
}
}
}
}
static int moveToRoot(BtCursor *pCur){
MemPage *pNew;
int rc;
Btree *pBt = pCur->pBt;
rc = sqlitepager_get(pBt->pPager, pCur->pgnoRoot, (void**)&pNew);
if( rc ) return rc;
rc = initPage(pBt, pNew, pCur->pgnoRoot, 0);
if( rc ) return rc;
sqlitepager_unref(pCur->pPage);
pCur->pPage = pNew;
pCur->idx = 0;
return SQLITE_OK;
}
static int moveToLeftmost(BtCursor *pCur){
Pgno pgno;
int rc;
while( (pgno = pCur->pPage->apCell[pCur->idx]->h.leftChild)!=0 ){
rc = moveToChild(pCur, pgno);
if( rc ) return rc;
}
return SQLITE_OK;
}
static int moveToRightmost(BtCursor *pCur){
Pgno pgno;
int rc;
while( (pgno = pCur->pPage->u.hdr.rightChild)!=0 ){
pCur->idx = pCur->pPage->nCell;
rc = moveToChild(pCur, pgno);
if( rc ) return rc;
}
pCur->idx = pCur->pPage->nCell - 1;
return SQLITE_OK;
}
static int fileBtreeFirst(BtCursor *pCur, int *pRes){
int rc;
if( pCur->pPage==0 ) return SQLITE_ABORT;
rc = moveToRoot(pCur);
if( rc ) return rc;
if( pCur->pPage->nCell==0 ){
*pRes = 1;
return SQLITE_OK;
}
*pRes = 0;
rc = moveToLeftmost(pCur);
pCur->eSkip = SKIP_NONE;
return rc;
}
static int fileBtreeLast(BtCursor *pCur, int *pRes){
int rc;
if( pCur->pPage==0 ) return SQLITE_ABORT;
rc = moveToRoot(pCur);
if( rc ) return rc;
assert( pCur->pPage->isInit );
if( pCur->pPage->nCell==0 ){
*pRes = 1;
return SQLITE_OK;
}
*pRes = 0;
rc = moveToRightmost(pCur);
pCur->eSkip = SKIP_NONE;
return rc;
}
static
int fileBtreeMoveto(BtCursor *pCur, const void *pKey, int nKey, int *pRes){
int rc;
if( pCur->pPage==0 ) return SQLITE_ABORT;
pCur->eSkip = SKIP_NONE;
rc = moveToRoot(pCur);
if( rc ) return rc;
for(;;){
int lwr, upr;
Pgno chldPg;
MemPage *pPage = pCur->pPage;
int c = -1;
lwr = 0;
upr = pPage->nCell-1;
while( lwr<=upr ){
pCur->idx = (lwr+upr)/2;
rc = fileBtreeKeyCompare(pCur, pKey, nKey, 0, &c);
if( rc ) return rc;
if( c==0 ){
pCur->iMatch = c;
if( pRes ) *pRes = 0;
return SQLITE_OK;
}
if( c<0 ){
lwr = pCur->idx+1;
}else{
upr = pCur->idx-1;
}
}
assert( lwr==upr+1 );
assert( pPage->isInit );
if( lwr>=pPage->nCell ){
chldPg = pPage->u.hdr.rightChild;
}else{
chldPg = pPage->apCell[lwr]->h.leftChild;
}
if( chldPg==0 ){
pCur->iMatch = c;
if( pRes ) *pRes = c;
return SQLITE_OK;
}
pCur->idx = lwr;
rc = moveToChild(pCur, chldPg);
if( rc ) return rc;
}
}
static int fileBtreeNext(BtCursor *pCur, int *pRes){
int rc;
MemPage *pPage = pCur->pPage;
assert( pRes!=0 );
if( pPage==0 ){
*pRes = 1;
return SQLITE_ABORT;
}
assert( pPage->isInit );
assert( pCur->eSkip!=SKIP_INVALID );
if( pPage->nCell==0 ){
*pRes = 1;
return SQLITE_OK;
}
assert( pCur->idx<pPage->nCell );
if( pCur->eSkip==SKIP_NEXT ){
pCur->eSkip = SKIP_NONE;
*pRes = 0;
return SQLITE_OK;
}
pCur->eSkip = SKIP_NONE;
pCur->idx++;
if( pCur->idx>=pPage->nCell ){
if( pPage->u.hdr.rightChild ){
rc = moveToChild(pCur, pPage->u.hdr.rightChild);
if( rc ) return rc;
rc = moveToLeftmost(pCur);
*pRes = 0;
return rc;
}
do{
if( pPage->pParent==0 ){
*pRes = 1;
return SQLITE_OK;
}
moveToParent(pCur);
pPage = pCur->pPage;
}while( pCur->idx>=pPage->nCell );
*pRes = 0;
return SQLITE_OK;
}
*pRes = 0;
if( pPage->u.hdr.rightChild==0 ){
return SQLITE_OK;
}
rc = moveToLeftmost(pCur);
return rc;
}
static int fileBtreePrevious(BtCursor *pCur, int *pRes){
int rc;
Pgno pgno;
MemPage *pPage;
pPage = pCur->pPage;
if( pPage==0 ){
*pRes = 1;
return SQLITE_ABORT;
}
assert( pPage->isInit );
assert( pCur->eSkip!=SKIP_INVALID );
if( pPage->nCell==0 ){
*pRes = 1;
return SQLITE_OK;
}
if( pCur->eSkip==SKIP_PREV ){
pCur->eSkip = SKIP_NONE;
*pRes = 0;
return SQLITE_OK;
}
pCur->eSkip = SKIP_NONE;
assert( pCur->idx>=0 );
if( (pgno = pPage->apCell[pCur->idx]->h.leftChild)!=0 ){
rc = moveToChild(pCur, pgno);
if( rc ) return rc;
rc = moveToRightmost(pCur);
}else{
while( pCur->idx==0 ){
if( pPage->pParent==0 ){
if( pRes ) *pRes = 1;
return SQLITE_OK;
}
moveToParent(pCur);
pPage = pCur->pPage;
}
pCur->idx--;
rc = SQLITE_OK;
}
*pRes = 0;
return rc;
}
static int allocatePage(Btree *pBt, MemPage **ppPage, Pgno *pPgno, Pgno nearby){
PageOne *pPage1 = pBt->page1;
int rc;
if( pPage1->freeList ){
OverflowPage *pOvfl;
FreelistInfo *pInfo;
rc = sqlitepager_write(pPage1);
if( rc ) return rc;
SWAB_ADD(pBt, pPage1->nFree, -1);
rc = sqlitepager_get(pBt->pPager, SWAB32(pBt, pPage1->freeList),
(void**)&pOvfl);
if( rc ) return rc;
rc = sqlitepager_write(pOvfl);
if( rc ){
sqlitepager_unref(pOvfl);
return rc;
}
pInfo = (FreelistInfo*)pOvfl->aPayload;
if( pInfo->nFree==0 ){
*pPgno = SWAB32(pBt, pPage1->freeList);
pPage1->freeList = pOvfl->iNext;
*ppPage = (MemPage*)pOvfl;
}else{
int closest, n;
n = SWAB32(pBt, pInfo->nFree);
if( n>1 && nearby>0 ){
int i, dist;
closest = 0;
dist = SWAB32(pBt, pInfo->aFree[0]) - nearby;
if( dist<0 ) dist = -dist;
for(i=1; i<n; i++){
int d2 = SWAB32(pBt, pInfo->aFree[i]) - nearby;
if( d2<0 ) d2 = -d2;
if( d2<dist ) closest = i;
}
}else{
closest = 0;
}
SWAB_ADD(pBt, pInfo->nFree, -1);
*pPgno = SWAB32(pBt, pInfo->aFree[closest]);
pInfo->aFree[closest] = pInfo->aFree[n-1];
rc = sqlitepager_get(pBt->pPager, *pPgno, (void**)ppPage);
sqlitepager_unref(pOvfl);
if( rc==SQLITE_OK ){
sqlitepager_dont_rollback(*ppPage);
rc = sqlitepager_write(*ppPage);
}
}
}else{
*pPgno = sqlitepager_pagecount(pBt->pPager) + 1;
rc = sqlitepager_get(pBt->pPager, *pPgno, (void**)ppPage);
if( rc ) return rc;
rc = sqlitepager_write(*ppPage);
}
return rc;
}
static int freePage(Btree *pBt, void *pPage, Pgno pgno){
PageOne *pPage1 = pBt->page1;
OverflowPage *pOvfl = (OverflowPage*)pPage;
int rc;
int needUnref = 0;
MemPage *pMemPage;
if( pgno==0 ){
assert( pOvfl!=0 );
pgno = sqlitepager_pagenumber(pOvfl);
}
assert( pgno>2 );
assert( sqlitepager_pagenumber(pOvfl)==pgno );
pMemPage = (MemPage*)pPage;
pMemPage->isInit = 0;
if( pMemPage->pParent ){
sqlitepager_unref(pMemPage->pParent);
pMemPage->pParent = 0;
}
rc = sqlitepager_write(pPage1);
if( rc ){
return rc;
}
SWAB_ADD(pBt, pPage1->nFree, 1);
if( pPage1->nFree!=0 && pPage1->freeList!=0 ){
OverflowPage *pFreeIdx;
rc = sqlitepager_get(pBt->pPager, SWAB32(pBt, pPage1->freeList),
(void**)&pFreeIdx);
if( rc==SQLITE_OK ){
FreelistInfo *pInfo = (FreelistInfo*)pFreeIdx->aPayload;
int n = SWAB32(pBt, pInfo->nFree);
if( n<(sizeof(pInfo->aFree)/sizeof(pInfo->aFree[0])) ){
rc = sqlitepager_write(pFreeIdx);
if( rc==SQLITE_OK ){
pInfo->aFree[n] = SWAB32(pBt, pgno);
SWAB_ADD(pBt, pInfo->nFree, 1);
sqlitepager_unref(pFreeIdx);
sqlitepager_dont_write(pBt->pPager, pgno);
return rc;
}
}
sqlitepager_unref(pFreeIdx);
}
}
if( pOvfl==0 ){
assert( pgno>0 );
rc = sqlitepager_get(pBt->pPager, pgno, (void**)&pOvfl);
if( rc ) return rc;
needUnref = 1;
}
rc = sqlitepager_write(pOvfl);
if( rc ){
if( needUnref ) sqlitepager_unref(pOvfl);
return rc;
}
pOvfl->iNext = pPage1->freeList;
pPage1->freeList = SWAB32(pBt, pgno);
memset(pOvfl->aPayload, 0, OVERFLOW_SIZE);
if( needUnref ) rc = sqlitepager_unref(pOvfl);
return rc;
}
static int clearCell(Btree *pBt, Cell *pCell){
Pager *pPager = pBt->pPager;
OverflowPage *pOvfl;
Pgno ovfl, nextOvfl;
int rc;
if( NKEY(pBt, pCell->h) + NDATA(pBt, pCell->h) <= MX_LOCAL_PAYLOAD ){
return SQLITE_OK;
}
ovfl = SWAB32(pBt, pCell->ovfl);
pCell->ovfl = 0;
while( ovfl ){
rc = sqlitepager_get(pPager, ovfl, (void**)&pOvfl);
if( rc ) return rc;
nextOvfl = SWAB32(pBt, pOvfl->iNext);
rc = freePage(pBt, pOvfl, ovfl);
if( rc ) return rc;
sqlitepager_unref(pOvfl);
ovfl = nextOvfl;
}
return SQLITE_OK;
}
static int fillInCell(
Btree *pBt,
Cell *pCell,
const void *pKey, int nKey,
const void *pData,int nData
){
OverflowPage *pOvfl, *pPrior;
Pgno *pNext;
int spaceLeft;
int n, rc;
int nPayload;
const char *pPayload;
char *pSpace;
Pgno nearby = 0;
pCell->h.leftChild = 0;
pCell->h.nKey = SWAB16(pBt, nKey & 0xffff);
pCell->h.nKeyHi = nKey >> 16;
pCell->h.nData = SWAB16(pBt, nData & 0xffff);
pCell->h.nDataHi = nData >> 16;
pCell->h.iNext = 0;
pNext = &pCell->ovfl;
pSpace = pCell->aPayload;
spaceLeft = MX_LOCAL_PAYLOAD;
pPayload = pKey;
pKey = 0;
nPayload = nKey;
pPrior = 0;
while( nPayload>0 ){
if( spaceLeft==0 ){
rc = allocatePage(pBt, (MemPage**)&pOvfl, pNext, nearby);
if( rc ){
*pNext = 0;
}else{
nearby = *pNext;
}
if( pPrior ) sqlitepager_unref(pPrior);
if( rc ){
clearCell(pBt, pCell);
return rc;
}
if( pBt->needSwab ) *pNext = swab32(*pNext);
pPrior = pOvfl;
spaceLeft = OVERFLOW_SIZE;
pSpace = pOvfl->aPayload;
pNext = &pOvfl->iNext;
}
n = nPayload;
if( n>spaceLeft ) n = spaceLeft;
memcpy(pSpace, pPayload, n);
nPayload -= n;
if( nPayload==0 && pData ){
pPayload = pData;
nPayload = nData;
pData = 0;
}else{
pPayload += n;
}
spaceLeft -= n;
pSpace += n;
}
*pNext = 0;
if( pPrior ){
sqlitepager_unref(pPrior);
}
return SQLITE_OK;
}
static void reparentPage(Pager *pPager, Pgno pgno, MemPage *pNewParent,int idx){
MemPage *pThis;
if( pgno==0 ) return;
assert( pPager!=0 );
pThis = sqlitepager_lookup(pPager, pgno);
if( pThis && pThis->isInit ){
if( pThis->pParent!=pNewParent ){
if( pThis->pParent ) sqlitepager_unref(pThis->pParent);
pThis->pParent = pNewParent;
if( pNewParent ) sqlitepager_ref(pNewParent);
}
pThis->idxParent = idx;
sqlitepager_unref(pThis);
}
}
static void reparentChildPages(Btree *pBt, MemPage *pPage){
int i;
Pager *pPager = pBt->pPager;
for(i=0; i<pPage->nCell; i++){
reparentPage(pPager, SWAB32(pBt, pPage->apCell[i]->h.leftChild), pPage, i);
}
reparentPage(pPager, SWAB32(pBt, pPage->u.hdr.rightChild), pPage, i);
pPage->idxShift = 0;
}
static void dropCell(Btree *pBt, MemPage *pPage, int idx, int sz){
int j;
assert( idx>=0 && idx<pPage->nCell );
assert( sz==cellSize(pBt, pPage->apCell[idx]) );
assert( sqlitepager_iswriteable(pPage) );
freeSpace(pBt, pPage, Addr(pPage->apCell[idx]) - Addr(pPage), sz);
for(j=idx; j<pPage->nCell-1; j++){
pPage->apCell[j] = pPage->apCell[j+1];
}
pPage->nCell--;
pPage->idxShift = 1;
}
static void insertCell(Btree *pBt, MemPage *pPage, int i, Cell *pCell, int sz){
int idx, j;
assert( i>=0 && i<=pPage->nCell );
assert( sz==cellSize(pBt, pCell) );
assert( sqlitepager_iswriteable(pPage) );
idx = allocateSpace(pBt, pPage, sz);
for(j=pPage->nCell; j>i; j--){
pPage->apCell[j] = pPage->apCell[j-1];
}
pPage->nCell++;
if( idx<=0 ){
pPage->isOverfull = 1;
pPage->apCell[i] = pCell;
}else{
memcpy(&pPage->u.aDisk[idx], pCell, sz);
pPage->apCell[i] = (Cell*)&pPage->u.aDisk[idx];
}
pPage->idxShift = 1;
}
static void relinkCellList(Btree *pBt, MemPage *pPage){
int i;
u16 *pIdx;
assert( sqlitepager_iswriteable(pPage) );
pIdx = &pPage->u.hdr.firstCell;
for(i=0; i<pPage->nCell; i++){
int idx = Addr(pPage->apCell[i]) - Addr(pPage);
assert( idx>0 && idx<SQLITE_USABLE_SIZE );
*pIdx = SWAB16(pBt, idx);
pIdx = &pPage->apCell[i]->h.iNext;
}
*pIdx = 0;
}
static void copyPage(MemPage *pTo, MemPage *pFrom){
uptr from, to;
int i;
memcpy(pTo->u.aDisk, pFrom->u.aDisk, SQLITE_USABLE_SIZE);
pTo->pParent = 0;
pTo->isInit = 1;
pTo->nCell = pFrom->nCell;
pTo->nFree = pFrom->nFree;
pTo->isOverfull = pFrom->isOverfull;
to = Addr(pTo);
from = Addr(pFrom);
for(i=0; i<pTo->nCell; i++){
uptr x = Addr(pFrom->apCell[i]);
if( x>from && x<from+SQLITE_USABLE_SIZE ){
*((uptr*)&pTo->apCell[i]) = x + to - from;
}else{
pTo->apCell[i] = pFrom->apCell[i];
}
}
}
#define NN 1
#define NB (NN*2+1)
static int balance(Btree *pBt, MemPage *pPage, BtCursor *pCur){
MemPage *pParent;
int nCell;
int nOld;
int nNew;
int nDiv;
int i, j, k;
int idx;
int nxDiv;
int rc;
int iCur;
MemPage *pOldCurPage;
int subtotal;
MemPage *extraUnref = 0;
MemPage *apOld[NB];
Pgno pgnoOld[NB];
MemPage *apNew[NB+1];
Pgno pgnoNew[NB+1];
int idxDiv[NB];
Cell *apDiv[NB];
Cell aTemp[NB];
int cntNew[NB+1];
int szNew[NB+1];
MemPage aOld[NB];
Cell *apCell[(MX_CELL+2)*NB];
int szCell[(MX_CELL+2)*NB];
assert( sqlitepager_iswriteable(pPage) );
if( !pPage->isOverfull && pPage->nFree<SQLITE_USABLE_SIZE/2
&& pPage->nCell>=2){
relinkCellList(pBt, pPage);
return SQLITE_OK;
}
pParent = pPage->pParent;
if( pParent==0 ){
Pgno pgnoChild;
MemPage *pChild;
assert( pPage->isInit );
if( pPage->nCell==0 ){
if( pPage->u.hdr.rightChild ){
pgnoChild = SWAB32(pBt, pPage->u.hdr.rightChild);
rc = sqlitepager_get(pBt->pPager, pgnoChild, (void**)&pChild);
if( rc ) return rc;
memcpy(pPage, pChild, SQLITE_USABLE_SIZE);
pPage->isInit = 0;
rc = initPage(pBt, pPage, sqlitepager_pagenumber(pPage), 0);
assert( rc==SQLITE_OK );
reparentChildPages(pBt, pPage);
if( pCur && pCur->pPage==pChild ){
sqlitepager_unref(pChild);
pCur->pPage = pPage;
sqlitepager_ref(pPage);
}
freePage(pBt, pChild, pgnoChild);
sqlitepager_unref(pChild);
}else{
relinkCellList(pBt, pPage);
}
return SQLITE_OK;
}
if( !pPage->isOverfull ){
relinkCellList(pBt, pPage);
return SQLITE_OK;
}
rc = sqlitepager_write(pPage);
if( rc ) return rc;
rc = allocatePage(pBt, &pChild, &pgnoChild, sqlitepager_pagenumber(pPage));
if( rc ) return rc;
assert( sqlitepager_iswriteable(pChild) );
copyPage(pChild, pPage);
pChild->pParent = pPage;
pChild->idxParent = 0;
sqlitepager_ref(pPage);
pChild->isOverfull = 1;
if( pCur && pCur->pPage==pPage ){
sqlitepager_unref(pPage);
pCur->pPage = pChild;
}else{
extraUnref = pChild;
}
zeroPage(pBt, pPage);
pPage->u.hdr.rightChild = SWAB32(pBt, pgnoChild);
pParent = pPage;
pPage = pChild;
}
rc = sqlitepager_write(pParent);
if( rc ) return rc;
assert( pParent->isInit );
if( pParent->idxShift ){
Pgno pgno, swabPgno;
pgno = sqlitepager_pagenumber(pPage);
swabPgno = SWAB32(pBt, pgno);
for(idx=0; idx<pParent->nCell; idx++){
if( pParent->apCell[idx]->h.leftChild==swabPgno ){
break;
}
}
assert( idx<pParent->nCell || pParent->u.hdr.rightChild==swabPgno );
}else{
idx = pPage->idxParent;
}
nOld = nNew = 0;
sqlitepager_ref(pParent);
nxDiv = idx - NN;
if( nxDiv + NB > pParent->nCell ){
nxDiv = pParent->nCell - NB + 1;
}
if( nxDiv<0 ){
nxDiv = 0;
}
nDiv = 0;
for(i=0, k=nxDiv; i<NB; i++, k++){
if( k<pParent->nCell ){
idxDiv[i] = k;
apDiv[i] = pParent->apCell[k];
nDiv++;
pgnoOld[i] = SWAB32(pBt, apDiv[i]->h.leftChild);
}else if( k==pParent->nCell ){
pgnoOld[i] = SWAB32(pBt, pParent->u.hdr.rightChild);
}else{
break;
}
rc = sqlitepager_get(pBt->pPager, pgnoOld[i], (void**)&apOld[i]);
if( rc ) goto balance_cleanup;
rc = initPage(pBt, apOld[i], pgnoOld[i], pParent);
if( rc ) goto balance_cleanup;
apOld[i]->idxParent = k;
nOld++;
}
if( pCur ){
iCur = 0;
for(i=0; i<nOld; i++){
if( pCur->pPage==apOld[i] ){
iCur += pCur->idx;
break;
}
iCur += apOld[i]->nCell;
if( i<nOld-1 && pCur->pPage==pParent && pCur->idx==idxDiv[i] ){
break;
}
iCur++;
}
pOldCurPage = pCur->pPage;
}
for(i=0; i<nOld; i++){
copyPage(&aOld[i], apOld[i]);
}
nCell = 0;
for(i=0; i<nOld; i++){
MemPage *pOld = &aOld[i];
for(j=0; j<pOld->nCell; j++){
apCell[nCell] = pOld->apCell[j];
szCell[nCell] = cellSize(pBt, apCell[nCell]);
nCell++;
}
if( i<nOld-1 ){
szCell[nCell] = cellSize(pBt, apDiv[i]);
memcpy(&aTemp[i], apDiv[i], szCell[nCell]);
apCell[nCell] = &aTemp[i];
dropCell(pBt, pParent, nxDiv, szCell[nCell]);
assert( SWAB32(pBt, apCell[nCell]->h.leftChild)==pgnoOld[i] );
apCell[nCell]->h.leftChild = pOld->u.hdr.rightChild;
nCell++;
}
}
for(subtotal=k=i=0; i<nCell; i++){
subtotal += szCell[i];
if( subtotal > USABLE_SPACE ){
szNew[k] = subtotal - szCell[i];
cntNew[k] = i;
subtotal = 0;
k++;
}
}
szNew[k] = subtotal;
cntNew[k] = nCell;
k++;
for(i=k-1; i>0; i--){
while( szNew[i]<USABLE_SPACE/2 ){
cntNew[i-1]--;
assert( cntNew[i-1]>0 );
szNew[i] += szCell[cntNew[i-1]];
szNew[i-1] -= szCell[cntNew[i-1]-1];
}
}
assert( cntNew[0]>0 );
for(i=0; i<k; i++){
if( i<nOld ){
apNew[i] = apOld[i];
pgnoNew[i] = pgnoOld[i];
apOld[i] = 0;
rc = sqlitepager_write(apNew[i]);
if( rc ) goto balance_cleanup;
}else{
rc = allocatePage(pBt, &apNew[i], &pgnoNew[i], pgnoNew[i-1]);
if( rc ) goto balance_cleanup;
}
nNew++;
zeroPage(pBt, apNew[i]);
apNew[i]->isInit = 1;
}
while( i<nOld ){
rc = freePage(pBt, apOld[i], pgnoOld[i]);
if( rc ) goto balance_cleanup;
sqlitepager_unref(apOld[i]);
apOld[i] = 0;
i++;
}
for(i=0; i<k-1; i++){
int minV = pgnoNew[i];
int minI = i;
for(j=i+1; j<k; j++){
if( pgnoNew[j]<(unsigned)minV ){
minI = j;
minV = pgnoNew[j];
}
}
if( minI>i ){
int t;
MemPage *pT;
t = pgnoNew[i];
pT = apNew[i];
pgnoNew[i] = pgnoNew[minI];
apNew[i] = apNew[minI];
pgnoNew[minI] = t;
apNew[minI] = pT;
}
}
j = 0;
for(i=0; i<nNew; i++){
MemPage *pNew = apNew[i];
while( j<cntNew[i] ){
assert( pNew->nFree>=szCell[j] );
if( pCur && iCur==j ){ pCur->pPage = pNew; pCur->idx = pNew->nCell; }
insertCell(pBt, pNew, pNew->nCell, apCell[j], szCell[j]);
j++;
}
assert( pNew->nCell>0 );
assert( !pNew->isOverfull );
relinkCellList(pBt, pNew);
if( i<nNew-1 && j<nCell ){
pNew->u.hdr.rightChild = apCell[j]->h.leftChild;
apCell[j]->h.leftChild = SWAB32(pBt, pgnoNew[i]);
if( pCur && iCur==j ){ pCur->pPage = pParent; pCur->idx = nxDiv; }
insertCell(pBt, pParent, nxDiv, apCell[j], szCell[j]);
j++;
nxDiv++;
}
}
assert( j==nCell );
apNew[nNew-1]->u.hdr.rightChild = aOld[nOld-1].u.hdr.rightChild;
if( nxDiv==pParent->nCell ){
pParent->u.hdr.rightChild = SWAB32(pBt, pgnoNew[nNew-1]);
}else{
pParent->apCell[nxDiv]->h.leftChild = SWAB32(pBt, pgnoNew[nNew-1]);
}
if( pCur ){
if( j<=iCur && pCur->pPage==pParent && pCur->idx>idxDiv[nOld-1] ){
assert( pCur->pPage==pOldCurPage );
pCur->idx += nNew - nOld;
}else{
assert( pOldCurPage!=0 );
sqlitepager_ref(pCur->pPage);
sqlitepager_unref(pOldCurPage);
}
}
for(i=0; i<nNew; i++){
reparentChildPages(pBt, apNew[i]);
}
reparentChildPages(pBt, pParent);
rc = balance(pBt, pParent, pCur);
balance_cleanup:
if( extraUnref ){
sqlitepager_unref(extraUnref);
}
for(i=0; i<nOld; i++){
if( apOld[i]!=0 && apOld[i]!=&aOld[i] ) sqlitepager_unref(apOld[i]);
}
for(i=0; i<nNew; i++){
sqlitepager_unref(apNew[i]);
}
if( pCur && pCur->pPage==0 ){
pCur->pPage = pParent;
pCur->idx = 0;
}else{
sqlitepager_unref(pParent);
}
return rc;
}
static int checkReadLocks(BtCursor *pCur){
BtCursor *p;
assert( pCur->wrFlag );
for(p=pCur->pShared; p!=pCur; p=p->pShared){
assert( p );
assert( p->pgnoRoot==pCur->pgnoRoot );
if( p->wrFlag==0 ) return SQLITE_LOCKED;
if( sqlitepager_pagenumber(p->pPage)!=p->pgnoRoot ){
moveToRoot(p);
}
}
return SQLITE_OK;
}
static int fileBtreeInsert(
BtCursor *pCur,
const void *pKey, int nKey,
const void *pData, int nData
){
Cell newCell;
int rc;
int loc;
int szNew;
MemPage *pPage;
Btree *pBt = pCur->pBt;
if( pCur->pPage==0 ){
return SQLITE_ABORT;
}
if( !pBt->inTrans || nKey+nData==0 ){
return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
}
assert( !pBt->readOnly );
if( !pCur->wrFlag ){
return SQLITE_PERM;
}
if( checkReadLocks(pCur) ){
return SQLITE_LOCKED;
}
rc = fileBtreeMoveto(pCur, pKey, nKey, &loc);
if( rc ) return rc;
pPage = pCur->pPage;
assert( pPage->isInit );
rc = sqlitepager_write(pPage);
if( rc ) return rc;
rc = fillInCell(pBt, &newCell, pKey, nKey, pData, nData);
if( rc ) return rc;
szNew = cellSize(pBt, &newCell);
if( loc==0 ){
newCell.h.leftChild = pPage->apCell[pCur->idx]->h.leftChild;
rc = clearCell(pBt, pPage->apCell[pCur->idx]);
if( rc ) return rc;
dropCell(pBt, pPage, pCur->idx, cellSize(pBt, pPage->apCell[pCur->idx]));
}else if( loc<0 && pPage->nCell>0 ){
assert( pPage->u.hdr.rightChild==0 );
pCur->idx++;
}else{
assert( pPage->u.hdr.rightChild==0 );
}
insertCell(pBt, pPage, pCur->idx, &newCell, szNew);
rc = balance(pCur->pBt, pPage, pCur);
pCur->eSkip = SKIP_INVALID;
return rc;
}
static int fileBtreeDelete(BtCursor *pCur){
MemPage *pPage = pCur->pPage;
Cell *pCell;
int rc;
Pgno pgnoChild;
Btree *pBt = pCur->pBt;
assert( pPage->isInit );
if( pCur->pPage==0 ){
return SQLITE_ABORT;
}
if( !pBt->inTrans ){
return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
}
assert( !pBt->readOnly );
if( pCur->idx >= pPage->nCell ){
return SQLITE_ERROR;
}
if( !pCur->wrFlag ){
return SQLITE_PERM;
}
if( checkReadLocks(pCur) ){
return SQLITE_LOCKED;
}
rc = sqlitepager_write(pPage);
if( rc ) return rc;
pCell = pPage->apCell[pCur->idx];
pgnoChild = SWAB32(pBt, pCell->h.leftChild);
rc = clearCell(pBt, pCell);
if( rc ) return rc;
if( pgnoChild ){
BtCursor leafCur;
Cell *pNext;
int szNext;
int notUsed;
getTempCursor(pCur, &leafCur);
rc = fileBtreeNext(&leafCur, ¬Used);
if( rc!=SQLITE_OK ){
if( rc!=SQLITE_NOMEM ) rc = SQLITE_CORRUPT;
return rc;
}
rc = sqlitepager_write(leafCur.pPage);
if( rc ) return rc;
dropCell(pBt, pPage, pCur->idx, cellSize(pBt, pCell));
pNext = leafCur.pPage->apCell[leafCur.idx];
szNext = cellSize(pBt, pNext);
pNext->h.leftChild = SWAB32(pBt, pgnoChild);
insertCell(pBt, pPage, pCur->idx, pNext, szNext);
rc = balance(pBt, pPage, pCur);
if( rc ) return rc;
pCur->eSkip = SKIP_NEXT;
dropCell(pBt, leafCur.pPage, leafCur.idx, szNext);
rc = balance(pBt, leafCur.pPage, pCur);
releaseTempCursor(&leafCur);
}else{
dropCell(pBt, pPage, pCur->idx, cellSize(pBt, pCell));
if( pCur->idx>=pPage->nCell ){
pCur->idx = pPage->nCell-1;
if( pCur->idx<0 ){
pCur->idx = 0;
pCur->eSkip = SKIP_NEXT;
}else{
pCur->eSkip = SKIP_PREV;
}
}else{
pCur->eSkip = SKIP_NEXT;
}
rc = balance(pBt, pPage, pCur);
}
return rc;
}
static int fileBtreeCreateTable(Btree *pBt, int *piTable){
MemPage *pRoot;
Pgno pgnoRoot;
int rc;
if( !pBt->inTrans ){
return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
}
if( pBt->readOnly ){
return SQLITE_READONLY;
}
rc = allocatePage(pBt, &pRoot, &pgnoRoot, 0);
if( rc ) return rc;
assert( sqlitepager_iswriteable(pRoot) );
zeroPage(pBt, pRoot);
sqlitepager_unref(pRoot);
*piTable = (int)pgnoRoot;
return SQLITE_OK;
}
static int clearDatabasePage(Btree *pBt, Pgno pgno, int freePageFlag){
MemPage *pPage;
int rc;
Cell *pCell;
int idx;
rc = sqlitepager_get(pBt->pPager, pgno, (void**)&pPage);
if( rc ) return rc;
rc = sqlitepager_write(pPage);
if( rc ) return rc;
rc = initPage(pBt, pPage, pgno, 0);
if( rc ) return rc;
idx = SWAB16(pBt, pPage->u.hdr.firstCell);
while( idx>0 ){
pCell = (Cell*)&pPage->u.aDisk[idx];
idx = SWAB16(pBt, pCell->h.iNext);
if( pCell->h.leftChild ){
rc = clearDatabasePage(pBt, SWAB32(pBt, pCell->h.leftChild), 1);
if( rc ) return rc;
}
rc = clearCell(pBt, pCell);
if( rc ) return rc;
}
if( pPage->u.hdr.rightChild ){
rc = clearDatabasePage(pBt, SWAB32(pBt, pPage->u.hdr.rightChild), 1);
if( rc ) return rc;
}
if( freePageFlag ){
rc = freePage(pBt, pPage, pgno);
}else{
zeroPage(pBt, pPage);
}
sqlitepager_unref(pPage);
return rc;
}
static int fileBtreeClearTable(Btree *pBt, int iTable){
int rc;
BtCursor *pCur;
if( !pBt->inTrans ){
return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
}
for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
if( pCur->pgnoRoot==(Pgno)iTable ){
if( pCur->wrFlag==0 ) return SQLITE_LOCKED;
moveToRoot(pCur);
}
}
rc = clearDatabasePage(pBt, (Pgno)iTable, 0);
if( rc ){
fileBtreeRollback(pBt);
}
return rc;
}
static int fileBtreeDropTable(Btree *pBt, int iTable){
int rc;
MemPage *pPage;
BtCursor *pCur;
if( !pBt->inTrans ){
return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
}
for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
if( pCur->pgnoRoot==(Pgno)iTable ){
return SQLITE_LOCKED;
}
}
rc = sqlitepager_get(pBt->pPager, (Pgno)iTable, (void**)&pPage);
if( rc ) return rc;
rc = fileBtreeClearTable(pBt, iTable);
if( rc ) return rc;
if( iTable>2 ){
rc = freePage(pBt, pPage, iTable);
}else{
zeroPage(pBt, pPage);
}
sqlitepager_unref(pPage);
return rc;
}
#if 0
static int copyCell(Btree *pBtFrom, BTree *pBtTo, Cell *pCell){
Pager *pFromPager = pBtFrom->pPager;
OverflowPage *pOvfl;
Pgno ovfl, nextOvfl;
Pgno *pPrev;
int rc = SQLITE_OK;
MemPage *pNew, *pPrevPg;
Pgno new;
if( NKEY(pBtTo, pCell->h) + NDATA(pBtTo, pCell->h) <= MX_LOCAL_PAYLOAD ){
return SQLITE_OK;
}
pPrev = &pCell->ovfl;
pPrevPg = 0;
ovfl = SWAB32(pBtTo, pCell->ovfl);
while( ovfl && rc==SQLITE_OK ){
rc = sqlitepager_get(pFromPager, ovfl, (void**)&pOvfl);
if( rc ) return rc;
nextOvfl = SWAB32(pBtFrom, pOvfl->iNext);
rc = allocatePage(pBtTo, &pNew, &new, 0);
if( rc==SQLITE_OK ){
rc = sqlitepager_write(pNew);
if( rc==SQLITE_OK ){
memcpy(pNew, pOvfl, SQLITE_USABLE_SIZE);
*pPrev = SWAB32(pBtTo, new);
if( pPrevPg ){
sqlitepager_unref(pPrevPg);
}
pPrev = &pOvfl->iNext;
pPrevPg = pNew;
}
}
sqlitepager_unref(pOvfl);
ovfl = nextOvfl;
}
if( pPrevPg ){
sqlitepager_unref(pPrevPg);
}
return rc;
}
#endif
#if 0
static int copyDatabasePage(
Btree *pBtFrom,
Pgno pgnoFrom,
Btree *pBtTo,
Pgno *pTo
){
MemPage *pPageFrom, *pPage;
Pgno to;
int rc;
Cell *pCell;
int idx;
rc = sqlitepager_get(pBtFrom->pPager, pgno, (void**)&pPageFrom);
if( rc ) return rc;
rc = allocatePage(pBt, &pPage, pTo, 0);
if( rc==SQLITE_OK ){
rc = sqlitepager_write(pPage);
}
if( rc==SQLITE_OK ){
memcpy(pPage, pPageFrom, SQLITE_USABLE_SIZE);
idx = SWAB16(pBt, pPage->u.hdr.firstCell);
while( idx>0 ){
pCell = (Cell*)&pPage->u.aDisk[idx];
idx = SWAB16(pBt, pCell->h.iNext);
if( pCell->h.leftChild ){
Pgno newChld;
rc = copyDatabasePage(pBtFrom, SWAB32(pBtFrom, pCell->h.leftChild),
pBtTo, &newChld);
if( rc ) return rc;
pCell->h.leftChild = SWAB32(pBtFrom, newChld);
}
rc = copyCell(pBtFrom, pBtTo, pCell);
if( rc ) return rc;
}
if( pPage->u.hdr.rightChild ){
Pgno newChld;
rc = copyDatabasePage(pBtFrom, SWAB32(pBtFrom, pPage->u.hdr.rightChild),
pBtTo, &newChld);
if( rc ) return rc;
pPage->u.hdr.rightChild = SWAB32(pBtTo, newChild);
}
}
sqlitepager_unref(pPage);
return rc;
}
#endif
static int fileBtreeGetMeta(Btree *pBt, int *aMeta){
PageOne *pP1;
int rc;
int i;
rc = sqlitepager_get(pBt->pPager, 1, (void**)&pP1);
if( rc ) return rc;
aMeta[0] = SWAB32(pBt, pP1->nFree);
for(i=0; i<sizeof(pP1->aMeta)/sizeof(pP1->aMeta[0]); i++){
aMeta[i+1] = SWAB32(pBt, pP1->aMeta[i]);
}
sqlitepager_unref(pP1);
return SQLITE_OK;
}
static int fileBtreeUpdateMeta(Btree *pBt, int *aMeta){
PageOne *pP1;
int rc, i;
if( !pBt->inTrans ){
return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
}
pP1 = pBt->page1;
rc = sqlitepager_write(pP1);
if( rc ) return rc;
for(i=0; i<sizeof(pP1->aMeta)/sizeof(pP1->aMeta[0]); i++){
pP1->aMeta[i] = SWAB32(pBt, aMeta[i+1]);
}
return SQLITE_OK;
}
#ifdef SQLITE_TEST
static int fileBtreePageDump(Btree *pBt, int pgno, int recursive){
int rc;
MemPage *pPage;
int i, j;
int nFree;
u16 idx;
char range[20];
unsigned char payload[20];
rc = sqlitepager_get(pBt->pPager, (Pgno)pgno, (void**)&pPage);
if( rc ){
return rc;
}
if( recursive ) printf("PAGE %d:\n", pgno);
i = 0;
idx = SWAB16(pBt, pPage->u.hdr.firstCell);
while( idx>0 && idx<=SQLITE_USABLE_SIZE-MIN_CELL_SIZE ){
Cell *pCell = (Cell*)&pPage->u.aDisk[idx];
int sz = cellSize(pBt, pCell);
sprintf(range,"%d..%d", idx, idx+sz-1);
sz = NKEY(pBt, pCell->h) + NDATA(pBt, pCell->h);
if( sz>sizeof(payload)-1 ) sz = sizeof(payload)-1;
memcpy(payload, pCell->aPayload, sz);
for(j=0; j<sz; j++){
if( payload[j]<0x20 || payload[j]>0x7f ) payload[j] = '.';
}
payload[sz] = 0;
printf(
"cell %2d: i=%-10s chld=%-4d nk=%-4d nd=%-4d payload=%s\n",
i, range, (int)pCell->h.leftChild,
NKEY(pBt, pCell->h), NDATA(pBt, pCell->h),
payload
);
if( pPage->isInit && pPage->apCell[i]!=pCell ){
printf("**** apCell[%d] does not match on prior entry ****\n", i);
}
i++;
idx = SWAB16(pBt, pCell->h.iNext);
}
if( idx!=0 ){
printf("ERROR: next cell index out of range: %d\n", idx);
}
printf("right_child: %d\n", SWAB32(pBt, pPage->u.hdr.rightChild));
nFree = 0;
i = 0;
idx = SWAB16(pBt, pPage->u.hdr.firstFree);
while( idx>0 && idx<SQLITE_USABLE_SIZE ){
FreeBlk *p = (FreeBlk*)&pPage->u.aDisk[idx];
sprintf(range,"%d..%d", idx, idx+p->iSize-1);
nFree += SWAB16(pBt, p->iSize);
printf("freeblock %2d: i=%-10s size=%-4d total=%d\n",
i, range, SWAB16(pBt, p->iSize), nFree);
idx = SWAB16(pBt, p->iNext);
i++;
}
if( idx!=0 ){
printf("ERROR: next freeblock index out of range: %d\n", idx);
}
if( recursive && pPage->u.hdr.rightChild!=0 ){
idx = SWAB16(pBt, pPage->u.hdr.firstCell);
while( idx>0 && idx<SQLITE_USABLE_SIZE-MIN_CELL_SIZE ){
Cell *pCell = (Cell*)&pPage->u.aDisk[idx];
fileBtreePageDump(pBt, SWAB32(pBt, pCell->h.leftChild), 1);
idx = SWAB16(pBt, pCell->h.iNext);
}
fileBtreePageDump(pBt, SWAB32(pBt, pPage->u.hdr.rightChild), 1);
}
sqlitepager_unref(pPage);
return SQLITE_OK;
}
#endif
#ifdef SQLITE_TEST
static int fileBtreeCursorDump(BtCursor *pCur, int *aResult){
int cnt, idx;
MemPage *pPage = pCur->pPage;
Btree *pBt = pCur->pBt;
aResult[0] = sqlitepager_pagenumber(pPage);
aResult[1] = pCur->idx;
aResult[2] = pPage->nCell;
if( pCur->idx>=0 && pCur->idx<pPage->nCell ){
aResult[3] = cellSize(pBt, pPage->apCell[pCur->idx]);
aResult[6] = SWAB32(pBt, pPage->apCell[pCur->idx]->h.leftChild);
}else{
aResult[3] = 0;
aResult[6] = 0;
}
aResult[4] = pPage->nFree;
cnt = 0;
idx = SWAB16(pBt, pPage->u.hdr.firstFree);
while( idx>0 && idx<SQLITE_USABLE_SIZE ){
cnt++;
idx = SWAB16(pBt, ((FreeBlk*)&pPage->u.aDisk[idx])->iNext);
}
aResult[5] = cnt;
aResult[7] = SWAB32(pBt, pPage->u.hdr.rightChild);
return SQLITE_OK;
}
#endif
static Pager *fileBtreePager(Btree *pBt){
return pBt->pPager;
}
typedef struct IntegrityCk IntegrityCk;
struct IntegrityCk {
Btree *pBt;
Pager *pPager;
int nPage;
int *anRef;
char *zErrMsg;
};
static void checkAppendMsg(IntegrityCk *pCheck, char *zMsg1, char *zMsg2){
if( pCheck->zErrMsg ){
char *zOld = pCheck->zErrMsg;
pCheck->zErrMsg = 0;
sqliteSetString(&pCheck->zErrMsg, zOld, "\n", zMsg1, zMsg2, (char*)0);
sqliteFree(zOld);
}else{
sqliteSetString(&pCheck->zErrMsg, zMsg1, zMsg2, (char*)0);
}
}
static int checkRef(IntegrityCk *pCheck, int iPage, char *zContext){
if( iPage==0 ) return 1;
if( iPage>pCheck->nPage || iPage<0 ){
char zBuf[100];
sprintf(zBuf, "invalid page number %d", iPage);
checkAppendMsg(pCheck, zContext, zBuf);
return 1;
}
if( pCheck->anRef[iPage]==1 ){
char zBuf[100];
sprintf(zBuf, "2nd reference to page %d", iPage);
checkAppendMsg(pCheck, zContext, zBuf);
return 1;
}
return (pCheck->anRef[iPage]++)>1;
}
static void checkList(
IntegrityCk *pCheck,
int isFreeList,
int iPage,
int N,
char *zContext
){
int i;
char zMsg[100];
while( N-- > 0 ){
OverflowPage *pOvfl;
if( iPage<1 ){
sprintf(zMsg, "%d pages missing from overflow list", N+1);
checkAppendMsg(pCheck, zContext, zMsg);
break;
}
if( checkRef(pCheck, iPage, zContext) ) break;
if( sqlitepager_get(pCheck->pPager, (Pgno)iPage, (void**)&pOvfl) ){
sprintf(zMsg, "failed to get page %d", iPage);
checkAppendMsg(pCheck, zContext, zMsg);
break;
}
if( isFreeList ){
FreelistInfo *pInfo = (FreelistInfo*)pOvfl->aPayload;
int n = SWAB32(pCheck->pBt, pInfo->nFree);
for(i=0; i<n; i++){
checkRef(pCheck, SWAB32(pCheck->pBt, pInfo->aFree[i]), zContext);
}
N -= n;
}
iPage = SWAB32(pCheck->pBt, pOvfl->iNext);
sqlitepager_unref(pOvfl);
}
}
static int keyCompare(
const char *zKey1, int nKey1,
const char *zKey2, int nKey2
){
int min = nKey1>nKey2 ? nKey2 : nKey1;
int c = memcmp(zKey1, zKey2, min);
if( c==0 ){
c = nKey1 - nKey2;
}
return c;
}
static int checkTreePage(
IntegrityCk *pCheck,
int iPage,
MemPage *pParent,
char *zParentContext,
char *zLowerBound,
int nLower,
char *zUpperBound,
int nUpper
){
MemPage *pPage;
int i, rc, depth, d2, pgno;
char *zKey1, *zKey2;
int nKey1, nKey2;
BtCursor cur;
Btree *pBt;
char zMsg[100];
char zContext[100];
char hit[SQLITE_USABLE_SIZE];
cur.pBt = pBt = pCheck->pBt;
if( iPage==0 ) return 0;
if( checkRef(pCheck, iPage, zParentContext) ) return 0;
sprintf(zContext, "On tree page %d: ", iPage);
if( (rc = sqlitepager_get(pCheck->pPager, (Pgno)iPage, (void**)&pPage))!=0 ){
sprintf(zMsg, "unable to get the page. error code=%d", rc);
checkAppendMsg(pCheck, zContext, zMsg);
return 0;
}
if( (rc = initPage(pBt, pPage, (Pgno)iPage, pParent))!=0 ){
sprintf(zMsg, "initPage() returns error code %d", rc);
checkAppendMsg(pCheck, zContext, zMsg);
sqlitepager_unref(pPage);
return 0;
}
depth = 0;
if( zLowerBound ){
zKey1 = sqliteMalloc( nLower+1 );
memcpy(zKey1, zLowerBound, nLower);
zKey1[nLower] = 0;
}else{
zKey1 = 0;
}
nKey1 = nLower;
cur.pPage = pPage;
for(i=0; i<pPage->nCell; i++){
Cell *pCell = pPage->apCell[i];
int sz;
nKey2 = NKEY(pBt, pCell->h);
sz = nKey2 + NDATA(pBt, pCell->h);
sprintf(zContext, "On page %d cell %d: ", iPage, i);
if( sz>MX_LOCAL_PAYLOAD ){
int nPage = (sz - MX_LOCAL_PAYLOAD + OVERFLOW_SIZE - 1)/OVERFLOW_SIZE;
checkList(pCheck, 0, SWAB32(pBt, pCell->ovfl), nPage, zContext);
}
cur.idx = i;
zKey2 = sqliteMallocRaw( nKey2+1 );
getPayload(&cur, 0, nKey2, zKey2);
if( zKey1 && keyCompare(zKey1, nKey1, zKey2, nKey2)>=0 ){
checkAppendMsg(pCheck, zContext, "Key is out of order");
}
pgno = SWAB32(pBt, pCell->h.leftChild);
d2 = checkTreePage(pCheck, pgno, pPage, zContext, zKey1,nKey1,zKey2,nKey2);
if( i>0 && d2!=depth ){
checkAppendMsg(pCheck, zContext, "Child page depth differs");
}
depth = d2;
sqliteFree(zKey1);
zKey1 = zKey2;
nKey1 = nKey2;
}
pgno = SWAB32(pBt, pPage->u.hdr.rightChild);
sprintf(zContext, "On page %d at right child: ", iPage);
checkTreePage(pCheck, pgno, pPage, zContext, zKey1,nKey1,zUpperBound,nUpper);
sqliteFree(zKey1);
memset(hit, 0, sizeof(hit));
memset(hit, 1, sizeof(PageHdr));
for(i=SWAB16(pBt, pPage->u.hdr.firstCell); i>0 && i<SQLITE_USABLE_SIZE; ){
Cell *pCell = (Cell*)&pPage->u.aDisk[i];
int j;
for(j=i+cellSize(pBt, pCell)-1; j>=i; j--) hit[j]++;
i = SWAB16(pBt, pCell->h.iNext);
}
for(i=SWAB16(pBt,pPage->u.hdr.firstFree); i>0 && i<SQLITE_USABLE_SIZE; ){
FreeBlk *pFBlk = (FreeBlk*)&pPage->u.aDisk[i];
int j;
for(j=i+SWAB16(pBt,pFBlk->iSize)-1; j>=i; j--) hit[j]++;
i = SWAB16(pBt,pFBlk->iNext);
}
for(i=0; i<SQLITE_USABLE_SIZE; i++){
if( hit[i]==0 ){
sprintf(zMsg, "Unused space at byte %d of page %d", i, iPage);
checkAppendMsg(pCheck, zMsg, 0);
break;
}else if( hit[i]>1 ){
sprintf(zMsg, "Multiple uses for byte %d of page %d", i, iPage);
checkAppendMsg(pCheck, zMsg, 0);
break;
}
}
#if 0
if( pParent && pParent->nCell>2 && pPage->nFree>3*SQLITE_USABLE_SIZE/4 ){
sprintf(zMsg, "free space (%d) greater than max (%d)", pPage->nFree,
SQLITE_USABLE_SIZE/3);
checkAppendMsg(pCheck, zContext, zMsg);
}
#endif
sqlitepager_unref(pPage);
return depth;
}
char *fileBtreeIntegrityCheck(Btree *pBt, int *aRoot, int nRoot){
int i;
int nRef;
IntegrityCk sCheck;
nRef = *sqlitepager_stats(pBt->pPager);
if( lockBtree(pBt)!=SQLITE_OK ){
return sqliteStrDup("Unable to acquire a read lock on the database");
}
sCheck.pBt = pBt;
sCheck.pPager = pBt->pPager;
sCheck.nPage = sqlitepager_pagecount(sCheck.pPager);
if( sCheck.nPage==0 ){
unlockBtreeIfUnused(pBt);
return 0;
}
sCheck.anRef = sqliteMallocRaw( (sCheck.nPage+1)*sizeof(sCheck.anRef[0]) );
sCheck.anRef[1] = 1;
for(i=2; i<=sCheck.nPage; i++){ sCheck.anRef[i] = 0; }
sCheck.zErrMsg = 0;
checkList(&sCheck, 1, SWAB32(pBt, pBt->page1->freeList),
SWAB32(pBt, pBt->page1->nFree), "Main freelist: ");
for(i=0; i<nRoot; i++){
if( aRoot[i]==0 ) continue;
checkTreePage(&sCheck, aRoot[i], 0, "List of tree roots: ", 0,0,0,0);
}
for(i=1; i<=sCheck.nPage; i++){
if( sCheck.anRef[i]==0 ){
char zBuf[100];
sprintf(zBuf, "Page %d is never used", i);
checkAppendMsg(&sCheck, zBuf, 0);
}
}
unlockBtreeIfUnused(pBt);
if( nRef != *sqlitepager_stats(pBt->pPager) ){
char zBuf[100];
sprintf(zBuf,
"Outstanding page count goes from %d to %d during this analysis",
nRef, *sqlitepager_stats(pBt->pPager)
);
checkAppendMsg(&sCheck, zBuf, 0);
}
sqliteFree(sCheck.anRef);
return sCheck.zErrMsg;
}
static const char *fileBtreeGetFilename(Btree *pBt){
assert( pBt->pPager!=0 );
return sqlitepager_filename(pBt->pPager);
}
static int fileBtreeCopyFile(Btree *pBtTo, Btree *pBtFrom){
int rc = SQLITE_OK;
Pgno i, nPage, nToPage;
if( !pBtTo->inTrans || !pBtFrom->inTrans ) return SQLITE_ERROR;
if( pBtTo->needSwab!=pBtFrom->needSwab ) return SQLITE_ERROR;
if( pBtTo->pCursor ) return SQLITE_BUSY;
memcpy(pBtTo->page1, pBtFrom->page1, SQLITE_USABLE_SIZE);
rc = sqlitepager_overwrite(pBtTo->pPager, 1, pBtFrom->page1);
nToPage = sqlitepager_pagecount(pBtTo->pPager);
nPage = sqlitepager_pagecount(pBtFrom->pPager);
for(i=2; rc==SQLITE_OK && i<=nPage; i++){
void *pPage;
rc = sqlitepager_get(pBtFrom->pPager, i, &pPage);
if( rc ) break;
rc = sqlitepager_overwrite(pBtTo->pPager, i, pPage);
if( rc ) break;
sqlitepager_unref(pPage);
}
for(i=nPage+1; rc==SQLITE_OK && i<=nToPage; i++){
void *pPage;
rc = sqlitepager_get(pBtTo->pPager, i, &pPage);
if( rc ) break;
rc = sqlitepager_write(pPage);
sqlitepager_unref(pPage);
sqlitepager_dont_write(pBtTo->pPager, i);
}
if( !rc && nPage<nToPage ){
rc = sqlitepager_truncate(pBtTo->pPager, nPage);
}
if( rc ){
fileBtreeRollback(pBtTo);
}
return rc;
}
static BtOps sqliteBtreeOps = {
fileBtreeClose,
fileBtreeSetCacheSize,
fileBtreeSetSafetyLevel,
fileBtreeBeginTrans,
fileBtreeCommit,
fileBtreeRollback,
fileBtreeBeginCkpt,
fileBtreeCommitCkpt,
fileBtreeRollbackCkpt,
fileBtreeCreateTable,
fileBtreeCreateTable,
fileBtreeDropTable,
fileBtreeClearTable,
fileBtreeCursor,
fileBtreeGetMeta,
fileBtreeUpdateMeta,
fileBtreeIntegrityCheck,
fileBtreeGetFilename,
fileBtreeCopyFile,
fileBtreePager,
#ifdef SQLITE_TEST
fileBtreePageDump,
#endif
};
static BtCursorOps sqliteBtreeCursorOps = {
fileBtreeMoveto,
fileBtreeDelete,
fileBtreeInsert,
fileBtreeFirst,
fileBtreeLast,
fileBtreeNext,
fileBtreePrevious,
fileBtreeKeySize,
fileBtreeKey,
fileBtreeKeyCompare,
fileBtreeDataSize,
fileBtreeData,
fileBtreeCloseCursor,
#ifdef SQLITE_TEST
fileBtreeCursorDump,
#endif
};