1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-08-08 14:02:16 +03:00

The btree.test test is no working with integrity_check enabled. (CVS 1330)

FossilOrigin-Name: 9f1caa530e69aaf202debac36b6a46d707f362d7
This commit is contained in:
drh
2004-05-09 11:51:38 +00:00
parent 457f501464
commit da200cc1c7
4 changed files with 319 additions and 129 deletions

View File

@@ -1,5 +1,5 @@
C Begin\strying\sto\sget\sintegrity\schecking\sworking\son\sthe\snew\sbtree.c.\s(CVS\s1329) C The\sbtree.test\stest\sis\sno\sworking\swith\sintegrity_check\senabled.\s(CVS\s1330)
D 2004-05-09T01:35:06 D 2004-05-09T11:51:39
F Makefile.in ab7b0d5118e2da97bac66be8684a1034e3500f5a F Makefile.in ab7b0d5118e2da97bac66be8684a1034e3500f5a
F Makefile.linux-gcc b86a99c493a5bfb402d1d9178dcdc4bd4b32f906 F Makefile.linux-gcc b86a99c493a5bfb402d1d9178dcdc4bd4b32f906
F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd
@@ -23,7 +23,7 @@ F sqlite.def fc4f5734786fe4743cfe2aa98eb2da4b089edb5f
F sqlite.pc.in 30552343140c53304c2a658c080fbe810cd09ca2 F sqlite.pc.in 30552343140c53304c2a658c080fbe810cd09ca2
F src/attach.c fa9a58234406d84eeb900517d0c0adc4b2da051a F src/attach.c fa9a58234406d84eeb900517d0c0adc4b2da051a
F src/auth.c a2a46e3ed7799134cf3d2dd5ae6650115f26b653 F src/auth.c a2a46e3ed7799134cf3d2dd5ae6650115f26b653
F src/btree.c 4c9b4a68f5e0c57382fca08d73426ca5af55c80f F src/btree.c c1251660eaefaf3b11798c8891c90f5412f7ecae
F src/btree.h 825034a68947baf99507f04f318f417013dcd3a3 F src/btree.h 825034a68947baf99507f04f318f417013dcd3a3
F src/btree_rb.c 47e5b5ec90846af392b5668b34648198ba459561 F src/btree_rb.c 47e5b5ec90846af392b5668b34648198ba459561
F src/build.c 21b6645c966970dac51bcccfa8650403a3f56210 F src/build.c 21b6645c966970dac51bcccfa8650403a3f56210
@@ -75,7 +75,7 @@ F test/auth.test 5c4d95cdaf539c0c236e20ce1f71a93e7dde9185
F test/bigfile.test ea904b853ce2d703b16c5ce90e2b54951bc1ae81 F test/bigfile.test ea904b853ce2d703b16c5ce90e2b54951bc1ae81
F test/bigrow.test 8ab252dba108f12ad64e337b0f2ff31a807ac578 F test/bigrow.test 8ab252dba108f12ad64e337b0f2ff31a807ac578
F test/bind.test 56a57043b42c4664ca705f6050e56717a8a6699a F test/bind.test 56a57043b42c4664ca705f6050e56717a8a6699a
F test/btree.test c4399a76bcc6d2c84b156e3a8fef29879d2e54c4 F test/btree.test ed5781db83b6c1de02e62781d44915a9abe3450a
F test/btree2.test 2ff77e0218e5f55ff5a85874f3e15c7859e3ac26 F test/btree2.test 2ff77e0218e5f55ff5a85874f3e15c7859e3ac26
F test/btree3.test e597fb59be2ac0ea69c62aaa2064e998e528b665 F test/btree3.test e597fb59be2ac0ea69c62aaa2064e998e528b665
F test/btree3rb.test 127efcf5cdfcc352054e7db12622b01cdd8b36ac F test/btree3rb.test 127efcf5cdfcc352054e7db12622b01cdd8b36ac
@@ -190,7 +190,7 @@ F www/sqlite.tcl 3c83b08cf9f18aa2d69453ff441a36c40e431604
F www/tclsqlite.tcl b9271d44dcf147a93c98f8ecf28c927307abd6da F www/tclsqlite.tcl b9271d44dcf147a93c98f8ecf28c927307abd6da
F www/vdbe.tcl 9b9095d4495f37697fd1935d10e14c6015e80aa1 F www/vdbe.tcl 9b9095d4495f37697fd1935d10e14c6015e80aa1
F www/whentouse.tcl a8335bce47cc2fddb07f19052cb0cb4d9129a8e4 F www/whentouse.tcl a8335bce47cc2fddb07f19052cb0cb4d9129a8e4
P ee706e9c74c3fb32fc3369db226fad9ed4db7596 P 499569daa6a3aed6609bcb1e11a3d231e13f4f9c
R cfbf2d289e390333f8744d3cd9325bcf R b0518d68837036ab90f8b4f001412933
U drh U drh
Z d0e25b8c23267f444288dc2922ce4fd0 Z 029dd36060965161aaa7a2d625e23da4

View File

@@ -1 +1 @@
499569daa6a3aed6609bcb1e11a3d231e13f4f9c 9f1caa530e69aaf202debac36b6a46d707f362d7

View File

@@ -9,7 +9,7 @@
** May you share freely, never taking more than you give. ** May you share freely, never taking more than you give.
** **
************************************************************************* *************************************************************************
** $Id: btree.c,v 1.118 2004/05/09 01:35:06 drh Exp $ ** $Id: btree.c,v 1.119 2004/05/09 11:51:39 drh Exp $
** **
** This file implements a external (disk-based) database using BTrees. ** This file implements a external (disk-based) database using BTrees.
** For a detailed discussion of BTrees, refer to ** For a detailed discussion of BTrees, refer to
@@ -218,6 +218,7 @@ struct MemPage {
u8 leaf; /* True if leaf flag is set */ u8 leaf; /* True if leaf flag is set */
u8 zeroData; /* True if zero data flag is set */ u8 zeroData; /* True if zero data flag is set */
u8 hdrOffset; /* 100 for page 1. 0 otherwise */ u8 hdrOffset; /* 100 for page 1. 0 otherwise */
u8 needRelink; /* True if need to run relinkCellList() */
int idxParent; /* Index in pParent->aCell[] of this node */ int idxParent; /* Index in pParent->aCell[] of this node */
int nFree; /* Number of free bytes on the page */ int nFree; /* Number of free bytes on the page */
int nCell; /* Number of entries on this page */ int nCell; /* Number of entries on this page */
@@ -377,6 +378,78 @@ static int cellSize(MemPage *pPage, unsigned char *pCell){
return n + nPayload; return n + nPayload;
} }
/*
** Do sanity checking on a page. Throw an exception if anything is
** not right.
**
** This routine is used for internal error checking only. It is omitted
** from most builds.
*/
#if defined(BTREE_DEBUG) && !defined(NDEBUG) && 0
static void _pageIntegrity(MemPage *pPage){
int pageSize;
u8 *data;
int i, idx, c, pc, hdr, nFree;
u8 used[MX_PAGE_SIZE];
pageSize = pPage->pBt->pageSize;
assert( pPage->aData==&((unsigned char*)pPage)[-pageSize] );
hdr = pPage->hdrOffset;
assert( hdr==(pPage->pgno==1 ? 100 : 0) );
assert( pPage->pgno==sqlite3pager_pagenumber(pPage->aData) );
c = pPage->aData[hdr];
if( pPage->isInit ){
assert( pPage->leaf == ((c & PTF_LEAF)!=0) );
assert( pPage->zeroData == ((c & PTF_ZERODATA)!=0) );
assert( pPage->intKey == ((c & PTF_INTKEY)!=0) );
}
data = pPage->aData;
memset(used, 0, pageSize);
for(i=0; i<hdr+10-pPage->leaf*4; i++) used[i] = 1;
nFree = 0;
pc = get2byte(&data[hdr+1]);
while( pc ){
int size;
assert( pc>0 && pc<pageSize-4 );
size = get2byte(&data[pc+2]);
assert( pc+size<=pageSize );
nFree += size;
for(i=pc; i<pc+size; i++){
assert( used[i]==0 );
used[i] = 1;
}
pc = get2byte(&data[pc]);
}
assert( pPage->isInit==0 || pPage->nFree==nFree+data[hdr+5] );
idx = 0;
pc = get2byte(&data[hdr+3]);
while( pc ){
int size;
assert( pPage->isInit==0 || idx<pPage->nCell );
assert( pc>0 && pc<pageSize-4 );
assert( pPage->isInit==0 || pPage->aCell[idx]==&data[pc] );
size = cellSize(pPage, &data[pc]);
assert( pc+size<=pageSize );
for(i=pc; i<pc+size; i++){
assert( used[i]==0 );
used[i] = 1;
}
pc = get2byte(&data[pc]);
idx++;
}
assert( idx==pPage->nCell );
nFree = 0;
for(i=0; i<pageSize; i++){
assert( used[i]<=1 );
if( used[i]==0 ) nFree++;
}
assert( nFree==data[hdr+5] );
}
#define pageIntegrity(X) _pageIntegrity(X)
#else
# define pageIntegrity(X)
#endif
/* /*
** Defragment the page given. All Cells are moved to the ** Defragment the page given. All Cells are moved to the
** beginning of the page and all free space is collected ** beginning of the page and all free space is collected
@@ -392,6 +465,8 @@ static void defragmentPage(MemPage *pPage){
assert( sqlite3pager_iswriteable(pPage->aData) ); assert( sqlite3pager_iswriteable(pPage->aData) );
assert( pPage->pBt!=0 ); assert( pPage->pBt!=0 );
assert( pPage->pBt->pageSize <= MX_PAGE_SIZE ); assert( pPage->pBt->pageSize <= MX_PAGE_SIZE );
assert( !pPage->needRelink );
assert( !pPage->isOverfull );
oldPage = pPage->aData; oldPage = pPage->aData;
hdr = pPage->hdrOffset; hdr = pPage->hdrOffset;
addr = 3+hdr; addr = 3+hdr;
@@ -408,9 +483,10 @@ static void defragmentPage(MemPage *pPage){
size = cellSize(pPage, &oldPage[pc]); size = cellSize(pPage, &oldPage[pc]);
memcpy(&newPage[n], &oldPage[pc], size); memcpy(&newPage[n], &oldPage[pc], size);
put2byte(&newPage[addr],n); put2byte(&newPage[addr],n);
assert( pPage->aCell[i]==&oldPage[pc] );
pPage->aCell[i++] = &oldPage[n]; pPage->aCell[i++] = &oldPage[n];
addr = n;
n += size; n += size;
addr = pc;
pc = get2byte(&oldPage[pc]); pc = get2byte(&oldPage[pc]);
} }
assert( i==pPage->nCell ); assert( i==pPage->nCell );
@@ -599,13 +675,12 @@ static int initPage(
assert( pParent==0 || pParent->pBt==pPage->pBt ); assert( pParent==0 || pParent->pBt==pPage->pBt );
assert( pPage->pgno==sqlite3pager_pagenumber(pPage->aData) ); assert( pPage->pgno==sqlite3pager_pagenumber(pPage->aData) );
assert( pPage->aData == &((unsigned char*)pPage)[-pPage->pBt->pageSize] ); assert( pPage->aData == &((unsigned char*)pPage)[-pPage->pBt->pageSize] );
assert( pPage->isInit==0 || pPage->pParent==pParent ); assert( pPage->pParent==0 || pPage->pParent==pParent );
if( pPage->isInit ) return SQLITE_OK; if( pPage->pParent==0 && pParent!=0 ){
assert( pPage->pParent==0 ); pPage->pParent = pParent;
pPage->pParent = pParent;
if( pParent ){
sqlite3pager_ref(pParent->aData); sqlite3pager_ref(pParent->aData);
} }
if( pPage->isInit ) return SQLITE_OK;
pPage->nCell = pPage->nCellAlloc = 0; pPage->nCell = pPage->nCellAlloc = 0;
assert( pPage->hdrOffset==(pPage->pgno==1 ? 100 : 0) ); assert( pPage->hdrOffset==(pPage->pgno==1 ? 100 : 0) );
hdr = pPage->hdrOffset; hdr = pPage->hdrOffset;
@@ -615,6 +690,7 @@ static int initPage(
pPage->zeroData = (c & PTF_ZERODATA)!=0; pPage->zeroData = (c & PTF_ZERODATA)!=0;
pPage->leaf = (c & PTF_LEAF)!=0; pPage->leaf = (c & PTF_LEAF)!=0;
pPage->isOverfull = 0; pPage->isOverfull = 0;
pPage->needRelink = 0;
pPage->idxShift = 0; pPage->idxShift = 0;
pageSize = pPage->pBt->pageSize; pageSize = pPage->pBt->pageSize;
@@ -657,6 +733,7 @@ static int initPage(
} }
pPage->isInit = 1; pPage->isInit = 1;
pageIntegrity(pPage);
return SQLITE_OK; return SQLITE_OK;
} }
@@ -670,6 +747,8 @@ static void zeroPage(MemPage *pPage, int flags){
int hdr = pPage->hdrOffset; int hdr = pPage->hdrOffset;
int first; int first;
assert( sqlite3pager_pagenumber(data)==pPage->pgno );
assert( &data[pBt->pageSize] == (unsigned char*)pPage );
assert( sqlite3pager_iswriteable(data) ); assert( sqlite3pager_iswriteable(data) );
memset(&data[hdr], 0, pBt->pageSize - hdr); memset(&data[hdr], 0, pBt->pageSize - hdr);
data[hdr] = flags; data[hdr] = flags;
@@ -685,6 +764,11 @@ static void zeroPage(MemPage *pPage, int flags){
pPage->leaf = (flags & PTF_LEAF)!=0; pPage->leaf = (flags & PTF_LEAF)!=0;
pPage->zeroData = (flags & PTF_ZERODATA)!=0; pPage->zeroData = (flags & PTF_ZERODATA)!=0;
pPage->hdrOffset = hdr; pPage->hdrOffset = hdr;
pPage->isOverfull = 0;
pPage->needRelink = 0;
pPage->idxShift = 0;
pPage->isInit = 1;
pageIntegrity(pPage);
} }
/* /*
@@ -745,6 +829,7 @@ static void releasePage(MemPage *pPage){
*/ */
static void pageDestructor(void *pData){ static void pageDestructor(void *pData){
MemPage *pPage = (MemPage*)&((char*)pData)[SQLITE_PAGE_SIZE]; MemPage *pPage = (MemPage*)&((char*)pData)[SQLITE_PAGE_SIZE];
assert( pPage->isInit==0 || pPage->needRelink==0 );
if( pPage->pParent ){ if( pPage->pParent ){
MemPage *pParent = pPage->pParent; MemPage *pParent = pPage->pParent;
pPage->pParent = 0; pPage->pParent = 0;
@@ -996,7 +1081,8 @@ static void invalidateCursors(Btree *pBt){
BtCursor *pCur; BtCursor *pCur;
for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){ for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
MemPage *pPage = pCur->pPage; MemPage *pPage = pCur->pPage;
if( pPage && !pPage->isInit ){ if( pPage /* && !pPage->isInit */ ){
pageIntegrity(pPage);
releasePage(pPage); releasePage(pPage);
pCur->pPage = 0; pCur->pPage = 0;
pCur->isValid = 0; pCur->isValid = 0;
@@ -1005,6 +1091,24 @@ static void invalidateCursors(Btree *pBt){
} }
} }
#ifdef SQLITE_TEST
/*
** Print debugging information about all cursors to standard output.
*/
void sqlite3BtreeCursorList(Btree *pBt){
BtCursor *pCur;
for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
MemPage *pPage = pCur->pPage;
char *zMode = pCur->wrFlag ? "rw" : "ro";
printf("CURSOR %08x rooted at %4d(%s) currently at %d.%d%s\n",
(int)pCur, pCur->pgnoRoot, zMode,
pPage ? pPage->pgno : 0, pCur->idx,
pCur->isValid ? "" : " eof"
);
}
}
#endif
/* /*
** Rollback the transaction in progress. All cursors will be ** Rollback the transaction in progress. All cursors will be
** invalided by this operation. Any attempt to use a cursor ** invalided by this operation. Any attempt to use a cursor
@@ -1267,6 +1371,7 @@ int sqlite3BtreeKeySize(BtCursor *pCur, u64 *pSize){
*pSize = 0; *pSize = 0;
}else{ }else{
pPage = pCur->pPage; pPage = pCur->pPage;
pageIntegrity(pPage);
assert( pPage!=0 ); assert( pPage!=0 );
assert( pCur->idx>=0 && pCur->idx<pPage->nCell ); assert( pCur->idx>=0 && pCur->idx<pPage->nCell );
cell = pPage->aCell[pCur->idx]; cell = pPage->aCell[pCur->idx];
@@ -1309,6 +1414,7 @@ static int getPayload(
assert( pCur->isValid ); assert( pCur->isValid );
pBt = pCur->pBt; pBt = pCur->pBt;
pPage = pCur->pPage; pPage = pCur->pPage;
pageIntegrity(pPage);
assert( pCur->idx>=0 && pCur->idx<pPage->nCell ); assert( pCur->idx>=0 && pCur->idx<pPage->nCell );
aPayload = pPage->aCell[pCur->idx]; aPayload = pPage->aCell[pCur->idx];
aPayload += 2; /* Skip the next cell index */ aPayload += 2; /* Skip the next cell index */
@@ -1431,6 +1537,7 @@ void *sqlite3BtreeKeyFetch(BtCursor *pCur){
assert( pCur->idx>=0 && pCur->idx<pCur->pPage->nCell ); assert( pCur->idx>=0 && pCur->idx<pCur->pPage->nCell );
pBt = pCur->pBt; pBt = pCur->pBt;
pPage = pCur->pPage; pPage = pCur->pPage;
pageIntegrity(pPage);
assert( pCur->idx>=0 && pCur->idx<pPage->nCell ); assert( pCur->idx>=0 && pCur->idx<pPage->nCell );
assert( pPage->intKey==0 ); assert( pPage->intKey==0 );
aPayload = pPage->aCell[pCur->idx]; aPayload = pPage->aCell[pCur->idx];
@@ -1467,6 +1574,7 @@ int sqlite3BtreeDataSize(BtCursor *pCur, u32 *pSize){
pPage = pCur->pPage; pPage = pCur->pPage;
assert( pPage!=0 ); assert( pPage!=0 );
assert( pPage->isInit ); assert( pPage->isInit );
pageIntegrity(pPage);
if( pPage->zeroData ){ if( pPage->zeroData ){
*pSize = 0; *pSize = 0;
}else{ }else{
@@ -1516,6 +1624,7 @@ static int moveToChild(BtCursor *pCur, u32 newPgno){
assert( pCur->isValid ); assert( pCur->isValid );
rc = getAndInitPage(pBt, newPgno, &pNewPage, pCur->pPage); rc = getAndInitPage(pBt, newPgno, &pNewPage, pCur->pPage);
if( rc ) return rc; if( rc ) return rc;
pageIntegrity(pNewPage);
pNewPage->idxParent = pCur->idx; pNewPage->idxParent = pCur->idx;
pOldPage = pCur->pPage; pOldPage = pCur->pPage;
pOldPage->idxShift = 0; pOldPage->idxShift = 0;
@@ -1539,8 +1648,9 @@ static int moveToChild(BtCursor *pCur, u32 newPgno){
*/ */
static int isRootPage(MemPage *pPage){ static int isRootPage(MemPage *pPage){
MemPage *pParent = pPage->pParent; MemPage *pParent = pPage->pParent;
assert( pParent==0 || pParent->isInit ); if( pParent==0 ) return 1;
if( pParent==0 || (pParent->pgno==1 && pParent->nCell==0) ) return 1; if( pParent->pgno>1 ) return 0;
if( get2byte(&pParent->aData[pParent->hdrOffset+3])==0 ) return 1;
return 0; return 0;
} }
@@ -1562,8 +1672,10 @@ static void moveToParent(BtCursor *pCur){
pPage = pCur->pPage; pPage = pCur->pPage;
assert( pPage!=0 ); assert( pPage!=0 );
assert( !isRootPage(pPage) ); assert( !isRootPage(pPage) );
pageIntegrity(pPage);
pParent = pPage->pParent; pParent = pPage->pParent;
assert( pParent!=0 ); assert( pParent!=0 );
pageIntegrity(pParent);
idxParent = pPage->idxParent; idxParent = pPage->idxParent;
sqlite3pager_ref(pParent->aData); sqlite3pager_ref(pParent->aData);
oldPgno = pPage->pgno; oldPgno = pPage->pgno;
@@ -1613,6 +1725,7 @@ static int moveToRoot(BtCursor *pCur){
return rc; return rc;
} }
releasePage(pCur->pPage); releasePage(pCur->pPage);
pageIntegrity(pRoot);
pCur->pPage = pRoot; pCur->pPage = pRoot;
pCur->idx = 0; pCur->idx = 0;
if( pRoot->nCell==0 && !pRoot->leaf ){ if( pRoot->nCell==0 && !pRoot->leaf ){
@@ -1761,6 +1874,7 @@ int sqlite3BtreeMoveto(BtCursor *pCur, const void *pKey, u64 nKey, int *pRes){
int c = -1; /* pRes return if table is empty must be -1 */ int c = -1; /* pRes return if table is empty must be -1 */
lwr = 0; lwr = 0;
upr = pPage->nCell-1; upr = pPage->nCell-1;
pageIntegrity(pPage);
while( lwr<=upr ){ while( lwr<=upr ){
void *pCellKey; void *pCellKey;
u64 nCellKey; u64 nCellKey;
@@ -2024,6 +2138,8 @@ static int freePage(MemPage *pPage){
if( n==0 ){ if( n==0 ){
/* This is the first free page */ /* This is the first free page */
rc = sqlite3pager_write(pPage->aData);
if( rc ) return rc;
memset(pPage->aData, 0, 8); memset(pPage->aData, 0, 8);
put4byte(&pPage1->aData[32], pPage->pgno); put4byte(&pPage1->aData[32], pPage->pgno);
}else{ }else{
@@ -2195,14 +2311,16 @@ static void reparentPage(Btree *pBt, Pgno pgno, MemPage *pNewParent, int idx){
if( pgno==0 ) return; if( pgno==0 ) return;
assert( pBt->pPager!=0 ); assert( pBt->pPager!=0 );
aData = sqlite3pager_lookup(pBt->pPager, pgno); aData = sqlite3pager_lookup(pBt->pPager, pgno);
pThis = (MemPage*)&aData[pBt->pageSize]; if( aData ){
if( pThis && pThis->isInit ){ pThis = (MemPage*)&aData[pBt->pageSize];
if( pThis->pParent!=pNewParent ){ if( pThis->isInit ){
if( pThis->pParent ) sqlite3pager_unref(pThis->pParent->aData); if( pThis->pParent!=pNewParent ){
pThis->pParent = pNewParent; if( pThis->pParent ) sqlite3pager_unref(pThis->pParent->aData);
if( pNewParent ) sqlite3pager_ref(pNewParent->aData); pThis->pParent = pNewParent;
if( pNewParent ) sqlite3pager_ref(pNewParent->aData);
}
pThis->idxParent = idx;
} }
pThis->idxParent = idx;
sqlite3pager_unref(aData); sqlite3pager_unref(aData);
} }
} }
@@ -2238,25 +2356,45 @@ static void reparentChildPages(MemPage *pPage){
** **
** "sz" must be the number of bytes in the cell. ** "sz" must be the number of bytes in the cell.
** **
** Do not bother maintaining the integrity of the linked list of Cells. ** Try to maintain the integrity of the linked list of cells. But if
** Only the pPage->aCell[] array is important. The relinkCellList() ** the cell being inserted does not fit on the page, this will not be
** routine will be called soon after this routine in order to rebuild ** possible. If the linked list is not maintained, then just update
** the linked list. ** pPage->aCell[] and set the pPage->needRelink flag so that we will
** know to rebuild the linked list later.
*/ */
static void dropCell(MemPage *pPage, int idx, int sz){ static void dropCell(MemPage *pPage, int idx, int sz){
int j, pc; int j, pc;
u8 *data;
assert( idx>=0 && idx<pPage->nCell ); assert( idx>=0 && idx<pPage->nCell );
assert( sz==cellSize(pPage, pPage->aCell[idx]) ); assert( sz==cellSize(pPage, pPage->aCell[idx]) );
assert( sqlite3pager_iswriteable(pPage->aData) ); assert( sqlite3pager_iswriteable(pPage->aData) );
assert( pPage->aCell[idx]>=pPage->aData ); assert( pPage->aCell[idx]>=pPage->aData );
assert( pPage->aCell[idx]<&pPage->aData[pPage->pBt->pageSize-sz] ); assert( pPage->aCell[idx]<&pPage->aData[pPage->pBt->pageSize-sz] );
pc = Addr(pPage->aCell[idx]) - Addr(pPage->aData); data = pPage->aData;
pc = Addr(pPage->aCell[idx]) - Addr(data);
assert( pc>pPage->hdrOffset && pc+sz<=pPage->pBt->pageSize ); assert( pc>pPage->hdrOffset && pc+sz<=pPage->pBt->pageSize );
freeSpace(pPage, pc, sz); freeSpace(pPage, pc, sz);
for(j=idx; j<pPage->nCell-1; j++){ for(j=idx; j<pPage->nCell-1; j++){
pPage->aCell[j] = pPage->aCell[j+1]; pPage->aCell[j] = pPage->aCell[j+1];
} }
pPage->nCell--; pPage->nCell--;
if( !pPage->isOverfull && !pPage->needRelink ){
u8 *pPrev;
if( idx==0 ){
pPrev = &data[pPage->hdrOffset+3];
}else{
pPrev = pPage->aCell[idx-1];
}
if( idx<pPage->nCell ){
pc = Addr(pPage->aCell[idx]) - Addr(data);
}else{
pc = 0;
}
put2byte(pPrev, pc);
pageIntegrity(pPage);
}else{
pPage->needRelink = 1;
}
pPage->idxShift = 1; pPage->idxShift = 1;
} }
@@ -2268,17 +2406,18 @@ static void dropCell(MemPage *pPage, int idx, int sz){
** will not fit, then just make pPage->aCell[i] point to the content ** will not fit, then just make pPage->aCell[i] point to the content
** and set pPage->isOverfull. ** and set pPage->isOverfull.
** **
** Do not bother maintaining the integrity of the linked list of Cells. ** Try to maintain the integrity of the linked list of cells. But if
** Only the pPage->aCell[] array is important. The relinkCellList() ** the cell being inserted does not fit on the page, this will not be
** routine will be called soon after this routine in order to rebuild ** possible. If the linked list is not maintained, then just update
** the linked list. ** pPage->aCell[] and set the pPage->needRelink flag so that we will
** know to rebuild the linked list later.
*/ */
static void insertCell(MemPage *pPage, int i, unsigned char *pCell, int sz){ static void insertCell(MemPage *pPage, int i, unsigned char *pCell, int sz){
int idx, j; int idx, j;
assert( i>=0 && i<=pPage->nCell ); assert( i>=0 && i<=pPage->nCell );
assert( sz==cellSize(pPage, pCell) ); assert( sz==cellSize(pPage, pCell) );
assert( sqlite3pager_iswriteable(pPage->aData) ); assert( sqlite3pager_iswriteable(pPage->aData) );
idx = allocateSpace(pPage, sz); idx = pPage->needRelink ? 0 : allocateSpace(pPage, sz);
resizeCellArray(pPage, pPage->nCell+1); resizeCellArray(pPage, pPage->nCell+1);
for(j=pPage->nCell; j>i; j--){ for(j=pPage->nCell; j>i; j--){
pPage->aCell[j] = pPage->aCell[j-1]; pPage->aCell[j] = pPage->aCell[j-1];
@@ -2288,8 +2427,24 @@ static void insertCell(MemPage *pPage, int i, unsigned char *pCell, int sz){
pPage->isOverfull = 1; pPage->isOverfull = 1;
pPage->aCell[i] = pCell; pPage->aCell[i] = pCell;
}else{ }else{
memcpy(&pPage->aData[idx], pCell, sz); u8 *data = pPage->aData;
pPage->aCell[i] = &pPage->aData[idx]; memcpy(&data[idx], pCell, sz);
pPage->aCell[i] = &data[idx];
}
if( !pPage->isOverfull && !pPage->needRelink ){
u8 *pPrev;
int pc;
if( i==0 ){
pPrev = &pPage->aData[pPage->hdrOffset+3];
}else{
pPrev = pPage->aCell[i-1];
}
pc = get2byte(pPrev);
put2byte(pPrev, idx);
put2byte(pPage->aCell[i], pc);
pageIntegrity(pPage);
}else{
pPage->needRelink = 1;
} }
pPage->idxShift = 1; pPage->idxShift = 1;
} }
@@ -2303,6 +2458,7 @@ static void insertCell(MemPage *pPage, int i, unsigned char *pCell, int sz){
static void relinkCellList(MemPage *pPage){ static void relinkCellList(MemPage *pPage){
int i, idxFrom; int i, idxFrom;
assert( sqlite3pager_iswriteable(pPage->aData) ); assert( sqlite3pager_iswriteable(pPage->aData) );
if( !pPage->needRelink ) return;
idxFrom = pPage->hdrOffset+3; idxFrom = pPage->hdrOffset+3;
for(i=0; i<pPage->nCell; i++){ for(i=0; i<pPage->nCell; i++){
int idx = Addr(pPage->aCell[i]) - Addr(pPage->aData); int idx = Addr(pPage->aCell[i]) - Addr(pPage->aData);
@@ -2311,6 +2467,7 @@ static void relinkCellList(MemPage *pPage){
idxFrom = idx; idxFrom = idx;
} }
put2byte(&pPage->aData[idxFrom], 0); put2byte(&pPage->aData[idxFrom], 0);
pPage->needRelink = 0;
} }
/* /*
@@ -2329,7 +2486,7 @@ static void relinkCellList(MemPage *pPage){
** **
** Over this operation completes, the meta data for pFrom is zeroed. ** Over this operation completes, the meta data for pFrom is zeroed.
*/ */
static void copyPage(MemPage *pTo, MemPage *pFrom){ static void movePage(MemPage *pTo, MemPage *pFrom){
uptr from, to; uptr from, to;
int i; int i;
int pageSize; int pageSize;
@@ -2356,6 +2513,15 @@ static void copyPage(MemPage *pTo, MemPage *pFrom){
} }
} }
/*
** For debugging...
*/
#if 1
# define TRACE(X) if( pager3_refinfo_enable ) printf X
#else
# define TRACE(X)
#endif
/* /*
** The following parameters determine how many adjacent pages get involved ** The following parameters determine how many adjacent pages get involved
** in a balancing operation. NN is the number of neighbors on either side ** in a balancing operation. NN is the number of neighbors on either side
@@ -2421,6 +2587,7 @@ static int balance(MemPage *pPage){
int usableSpace; /* Bytes in pPage beyond the header */ int usableSpace; /* Bytes in pPage beyond the header */
int pageFlags; /* Value of pPage->aData[0] */ int pageFlags; /* Value of pPage->aData[0] */
int subtotal; /* Subtotal of bytes in cells on one page */ int subtotal; /* Subtotal of bytes in cells on one page */
MemPage *extraUnref = 0; /* Unref this page if not zero */
MemPage *apOld[NB]; /* pPage and up to two siblings */ MemPage *apOld[NB]; /* pPage and up to two siblings */
Pgno pgnoOld[NB]; /* Page numbers for each page in apOld[] */ Pgno pgnoOld[NB]; /* Page numbers for each page in apOld[] */
MemPage *apCopy[NB]; /* Private copies of apOld[] pages */ MemPage *apCopy[NB]; /* Private copies of apOld[] pages */
@@ -2451,6 +2618,7 @@ static int balance(MemPage *pPage){
** it means this page is the root page and special rules apply. ** it means this page is the root page and special rules apply.
*/ */
pParent = pPage->pParent; pParent = pPage->pParent;
TRACE(("BALANCE: begin page %d\n", pPage->pgno));
if( pParent==0 ){ if( pParent==0 ){
Pgno pgnoChild; Pgno pgnoChild;
MemPage *pChild; MemPage *pChild;
@@ -2459,6 +2627,7 @@ static int balance(MemPage *pPage){
if( pPage->leaf ){ if( pPage->leaf ){
/* The table is completely empty */ /* The table is completely empty */
relinkCellList(pPage); relinkCellList(pPage);
TRACE(("BALANCE: empty table\n"));
}else{ }else{
/* The root page is empty but has one child. Transfer the /* The root page is empty but has one child. Transfer the
** information from that one child into the root page if it ** information from that one child into the root page if it
@@ -2489,9 +2658,11 @@ static int balance(MemPage *pPage){
cellSize(pChild, pChild->aCell[i])); cellSize(pChild, pChild->aCell[i]));
} }
freePage(pChild); freePage(pChild);
TRACE(("BALANCE: child %d transfer to page 1\n", pChild->pgno));
}else{ }else{
/* The child has more information that will fit on the root. /* The child has more information that will fit on the root.
** The tree is already balanced. Do nothing. */ ** The tree is already balanced. Do nothing. */
TRACE(("BALANCE: child %d will not fit on page 1\n", pChild->pgno));
} }
}else{ }else{
memcpy(pPage, pChild, pBt->pageSize); memcpy(pPage, pChild, pBt->pageSize);
@@ -2500,6 +2671,7 @@ static int balance(MemPage *pPage){
rc = initPage(pPage, 0); rc = initPage(pPage, 0);
assert( rc==SQLITE_OK ); assert( rc==SQLITE_OK );
freePage(pChild); freePage(pChild);
TRACE(("BALANCE: transfer child %d into root\n", pChild->pgno));
} }
reparentChildPages(pPage); reparentChildPages(pPage);
releasePage(pChild); releasePage(pChild);
@@ -2510,6 +2682,7 @@ static int balance(MemPage *pPage){
/* It is OK for the root page to be less than half full. /* It is OK for the root page to be less than half full.
*/ */
relinkCellList(pPage); relinkCellList(pPage);
TRACE(("BALANCE: Root page is underfull but that is ok\n"));
return SQLITE_OK; return SQLITE_OK;
} }
/* /*
@@ -2523,17 +2696,18 @@ static int balance(MemPage *pPage){
rc = allocatePage(pBt, &pChild, &pgnoChild, pPage->pgno); rc = allocatePage(pBt, &pChild, &pgnoChild, pPage->pgno);
if( rc ) return rc; if( rc ) return rc;
assert( sqlite3pager_iswriteable(pChild->aData) ); assert( sqlite3pager_iswriteable(pChild->aData) );
copyPage(pChild, pPage); movePage(pChild, pPage);
assert( pChild->aData[0]==pPage->aData[pPage->hdrOffset] ); assert( pChild->aData[0]==pPage->aData[pPage->hdrOffset] );
pChild->pParent = pPage; pChild->pParent = pPage;
pChild->idxParent = 0;
sqlite3pager_ref(pPage->aData); sqlite3pager_ref(pPage->aData);
pChild->idxParent = 0;
pChild->isOverfull = 1; pChild->isOverfull = 1;
zeroPage(pPage, pChild->aData[0] & ~PTF_LEAF); zeroPage(pPage, pChild->aData[0] & ~PTF_LEAF);
put4byte(&pPage->aData[pPage->hdrOffset+6], pChild->pgno); put4byte(&pPage->aData[pPage->hdrOffset+6], pChild->pgno);
pParent = pPage; pParent = pPage;
pPage = pChild; pPage = pChild;
initPage(pParent, 0); extraUnref = pChild;
TRACE(("BALANCE: Copy root into %d and blance\n", pPage->pgno));
} }
rc = sqlite3pager_write(pParent->aData); rc = sqlite3pager_write(pParent->aData);
if( rc ) return rc; if( rc ) return rc;
@@ -2610,7 +2784,9 @@ static int balance(MemPage *pPage){
for(i=0; i<nOld; i++){ for(i=0; i<nOld; i++){
MemPage *p = apCopy[i] = (MemPage*)&aCopy[i+1][-sizeof(MemPage)]; MemPage *p = apCopy[i] = (MemPage*)&aCopy[i+1][-sizeof(MemPage)];
p->aData = &((u8*)p)[-pBt->pageSize]; p->aData = &((u8*)p)[-pBt->pageSize];
copyPage(p, apOld[i]); p->aCell = 0;
p->hdrOffset = 0;
movePage(p, apOld[i]);
} }
/* /*
@@ -2635,11 +2811,12 @@ static int balance(MemPage *pPage){
nCell++; nCell++;
} }
if( i<nOld-1 ){ if( i<nOld-1 ){
szCell[nCell] = cellSize(pParent, apDiv[i]) - leafCorrection; szCell[nCell] = cellSize(pParent, apDiv[i]);
memcpy(aTemp[i], apDiv[i], szCell[nCell] + leafCorrection); memcpy(aTemp[i], apDiv[i], szCell[nCell]);
apCell[nCell] = &aTemp[i][leafCorrection]; apCell[nCell] = &aTemp[i][leafCorrection];
dropCell(pParent, nxDiv, szCell[nCell]); dropCell(pParent, nxDiv, szCell[nCell]);
assert( get4byte(&apCell[nCell][2])==pgnoOld[i] ); szCell[nCell] -= leafCorrection;
assert( get4byte(&aTemp[i][2])==pgnoOld[i] );
if( !pOld->leaf ){ if( !pOld->leaf ){
assert( leafCorrection==0 ); assert( leafCorrection==0 );
/* The right pointer of the child page pOld becomes the left /* The right pointer of the child page pOld becomes the left
@@ -2691,18 +2868,19 @@ static int balance(MemPage *pPage){
assert( pPage->pgno>1 ); assert( pPage->pgno>1 );
pageFlags = pPage->aData[0]; pageFlags = pPage->aData[0];
for(i=0; i<k; i++){ for(i=0; i<k; i++){
MemPage *pNew;
if( i<nOld ){ if( i<nOld ){
apNew[i] = apOld[i]; pNew = apNew[i] = apOld[i];
pgnoNew[i] = pgnoOld[i]; pgnoNew[i] = pgnoOld[i];
apOld[i] = 0; apOld[i] = 0;
sqlite3pager_write(apNew[i]->aData); sqlite3pager_write(pNew->aData);
}else{ }else{
rc = allocatePage(pBt, &apNew[i], &pgnoNew[i], pgnoNew[i-1]); rc = allocatePage(pBt, &pNew, &pgnoNew[i], pgnoNew[i-1]);
if( rc ) goto balance_cleanup; if( rc ) goto balance_cleanup;
apNew[i] = pNew;
} }
nNew++; nNew++;
zeroPage(apNew[i], pageFlags); zeroPage(pNew, pageFlags);
apNew[i]->isInit = 1;
} }
/* Free any old pages that were not reused as new pages. /* Free any old pages that were not reused as new pages.
@@ -2710,7 +2888,7 @@ static int balance(MemPage *pPage){
while( i<nOld ){ while( i<nOld ){
rc = freePage(apOld[i]); rc = freePage(apOld[i]);
if( rc ) goto balance_cleanup; if( rc ) goto balance_cleanup;
sqlite3pager_unref(apOld[i]->aData); releasePage(apOld[i]);
apOld[i] = 0; apOld[i] = 0;
i++; i++;
} }
@@ -2804,7 +2982,11 @@ static int balance(MemPage *pPage){
/* /*
** balance the parent page. ** balance the parent page.
*/ */
assert( pPage->isInit );
assert( pParent->isInit );
pageIntegrity(pPage);
rc = balance(pParent); rc = balance(pParent);
/* /*
** Cleanup before returning. ** Cleanup before returning.
@@ -2813,7 +2995,6 @@ balance_cleanup:
for(i=0; i<nOld; i++){ for(i=0; i<nOld; i++){
releasePage(apOld[i]); releasePage(apOld[i]);
if( apCopy[i] ){ if( apCopy[i] ){
releasePage(apCopy[i]->pParent);
sqliteFree(apCopy[i]->aCell); sqliteFree(apCopy[i]->aCell);
} }
} }
@@ -2821,6 +3002,8 @@ balance_cleanup:
releasePage(apNew[i]); releasePage(apNew[i]);
} }
releasePage(pParent); releasePage(pParent);
releasePage(extraUnref);
TRACE(("BALANCE: Finished with %d\n", pPage->pgno));
return rc; return rc;
} }
@@ -2972,6 +3155,7 @@ int sqlite3BtreeDelete(BtCursor *pCur){
unsigned char *pNext; unsigned char *pNext;
int szNext; int szNext;
int notUsed; int notUsed;
unsigned char tempbuf[4];
getTempCursor(pCur, &leafCur); getTempCursor(pCur, &leafCur);
rc = sqlite3BtreeNext(&leafCur, &notUsed); rc = sqlite3BtreeNext(&leafCur, &notUsed);
if( rc!=SQLITE_OK ){ if( rc!=SQLITE_OK ){
@@ -2983,10 +3167,12 @@ int sqlite3BtreeDelete(BtCursor *pCur){
dropCell(pPage, pCur->idx, cellSize(pPage, pCell)); dropCell(pPage, pCur->idx, cellSize(pPage, pCell));
pNext = leafCur.pPage->aCell[leafCur.idx]; pNext = leafCur.pPage->aCell[leafCur.idx];
szNext = cellSize(leafCur.pPage, pNext); szNext = cellSize(leafCur.pPage, pNext);
insertCell(pPage, pCur->idx, &pNext[-4], szNext+4); memcpy(tempbuf, &pNext[-2], 4);
put4byte(&pNext[-2], pgnoChild); put4byte(&pNext[-2], pgnoChild);
insertCell(pPage, pCur->idx, &pNext[-4], szNext+4);
rc = balance(pPage); rc = balance(pPage);
if( rc ) return rc; if( rc ) return rc;
memcpy(&pNext[-2], tempbuf, 4);
dropCell(leafCur.pPage, leafCur.idx, szNext); dropCell(leafCur.pPage, leafCur.idx, szNext);
rc = balance(leafCur.pPage); rc = balance(leafCur.pPage);
releaseTempCursor(&leafCur); releaseTempCursor(&leafCur);
@@ -3019,7 +3205,7 @@ int sqlite3BtreeCreateTable(Btree *pBt, int *piTable, int flags){
if( pBt->readOnly ){ if( pBt->readOnly ){
return SQLITE_READONLY; return SQLITE_READONLY;
} }
rc = allocatePage(pBt, &pRoot, &pgnoRoot, 0); rc = allocatePage(pBt, &pRoot, &pgnoRoot, 1);
if( rc ) return rc; if( rc ) return rc;
assert( sqlite3pager_iswriteable(pRoot->aData) ); assert( sqlite3pager_iswriteable(pRoot->aData) );
zeroPage(pRoot, flags | PTF_LEAF); zeroPage(pRoot, flags | PTF_LEAF);
@@ -3190,8 +3376,9 @@ int sqlite3BtreePageDump(Btree *pBt, int pgno, int recursive){
pPage->intKey = (c & PTF_INTKEY)!=0; pPage->intKey = (c & PTF_INTKEY)!=0;
pPage->zeroData = (c & PTF_ZERODATA)!=0; pPage->zeroData = (c & PTF_ZERODATA)!=0;
pPage->leaf = (c & PTF_LEAF)!=0; pPage->leaf = (c & PTF_LEAF)!=0;
printf("PAGE %d: flags=0x%02x frag=%d\n", pgno, printf("PAGE %d: flags=0x%02x frag=%d parent=%d\n", pgno,
data[hdr], data[hdr+5]); data[hdr], data[hdr+5],
(pPage->isInit && pPage->pParent) ? pPage->pParent->pgno : 0);
i = 0; i = 0;
assert( hdr == (pgno==1 ? 100 : 0) ); assert( hdr == (pgno==1 ? 100 : 0) );
idx = get2byte(&data[hdr+3]); idx = get2byte(&data[hdr+3]);
@@ -3287,9 +3474,11 @@ int sqlite3BtreeFlags(BtCursor *pCur){
** **
** This routine is used for testing and debugging only. ** This routine is used for testing and debugging only.
*/ */
int sqlite3BtreeCursorDump(BtCursor *pCur, int *aResult){ int sqlite3BtreeCursorInfo(BtCursor *pCur, int *aResult){
int cnt, idx; int cnt, idx;
MemPage *pPage = pCur->pPage; MemPage *pPage = pCur->pPage;
pageIntegrity(pPage);
assert( pPage->isInit ); assert( pPage->isInit );
aResult[0] = sqlite3pager_pagenumber(pPage->aData); aResult[0] = sqlite3pager_pagenumber(pPage->aData);
assert( aResult[0]==pPage->pgno ); assert( aResult[0]==pPage->pgno );
@@ -3439,9 +3628,9 @@ static int keyCompare(
** **
** 1. Make sure that cells and freeblocks do not overlap ** 1. Make sure that cells and freeblocks do not overlap
** but combine to completely cover the page. ** but combine to completely cover the page.
** 2. Make sure cell keys are in order. ** NO 2. Make sure cell keys are in order.
** 3. Make sure no key is less than or equal to zLowerBound. ** NO 3. Make sure no key is less than or equal to zLowerBound.
** 4. Make sure no key is greater than or equal to zUpperBound. ** NO 4. Make sure no key is greater than or equal to zUpperBound.
** 5. Check the integrity of overflow pages. ** 5. Check the integrity of overflow pages.
** 6. Recursively call checkTreePage on all children. ** 6. Recursively call checkTreePage on all children.
** 7. Verify that the depth of all children is the same. ** 7. Verify that the depth of all children is the same.
@@ -3459,18 +3648,23 @@ static int checkTreePage(
int nUpper /* Number of characters in zUpperBound */ int nUpper /* Number of characters in zUpperBound */
){ ){
MemPage *pPage; MemPage *pPage;
int i, rc, depth, d2, pgno; int i, rc, depth, d2, pgno, cnt;
int hdr;
u8 *data;
char *zKey1, *zKey2; char *zKey1, *zKey2;
int nKey1, nKey2; int nKey1, nKey2;
BtCursor cur; BtCursor cur;
Btree *pBt; Btree *pBt;
int maxLocal, pageSize;
char zMsg[100]; char zMsg[100];
char zContext[100]; char zContext[100];
char hit[SQLITE_USABLE_SIZE]; char hit[MX_PAGE_SIZE];
/* Check that the page exists /* Check that the page exists
*/ */
cur.pBt = pBt = pCheck->pBt; cur.pBt = pBt = pCheck->pBt;
maxLocal = pBt->maxLocal;
pageSize = pBt->pageSize;
if( iPage==0 ) return 0; if( iPage==0 ) return 0;
if( checkRef(pCheck, iPage, zParentContext) ) return 0; if( checkRef(pCheck, iPage, zParentContext) ) return 0;
sprintf(zContext, "On tree page %d: ", iPage); sprintf(zContext, "On tree page %d: ", iPage);
@@ -3486,92 +3680,77 @@ static int checkTreePage(
return 0; return 0;
} }
#if 0
/* Check out all the cells. /* Check out all the cells.
*/ */
depth = 0; depth = 0;
if( zLowerBound ){
zKey1 = sqliteMalloc( nLower+1 );
memcpy(zKey1, zLowerBound, nLower);
zKey1[nLower] = 0;
}else{
zKey1 = 0;
}
nKey1 = nLower;
cur.pPage = pPage; cur.pPage = pPage;
for(i=0; i<pPage->nCell; i++){ for(i=0; i<pPage->nCell; i++){
Cell *pCell = pPage->aCell[i]; u8 *pCell = pPage->aCell[i];
int sz; u64 nKey, nData;
int sz, nHeader;
/* Check payload overflow pages /* Check payload overflow pages
*/ */
nKey2 = NKEY(pBt, pCell->h); parseCellHeader(pPage, pCell, &nData, &nKey, &nHeader);
sz = nKey2 + NDATA(pBt, pCell->h); sz = nData;
sprintf(zContext, "On page %d cell %d: ", iPage, i); if( !pPage->intKey ) sz += nKey;
if( sz>MX_LOCAL_PAYLOAD ){ if( sz>maxLocal ){
int nPage = (sz - MX_LOCAL_PAYLOAD + OVERFLOW_SIZE - 1)/OVERFLOW_SIZE; int nPage = (sz - maxLocal + pageSize - 5)/(pageSize - 4);
checkList(pCheck, 0, SWAB32(pBt, pCell->ovfl), nPage, zContext); checkList(pCheck, 0, get4byte(&pCell[nHeader+maxLocal]),nPage,zContext);
}
/* Check that keys are in the right order
*/
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");
} }
/* Check sanity of left child page. /* Check sanity of left child page.
*/ */
pgno = SWAB32(pBt, pCell->h.leftChild); if( !pPage->leaf ){
d2 = checkTreePage(pCheck, pgno, pPage, zContext, zKey1,nKey1,zKey2,nKey2); pgno = get4byte(&pCell[2]);
if( i>0 && d2!=depth ){ d2 = checkTreePage(pCheck,pgno,pPage,zContext,0,0,0,0);
checkAppendMsg(pCheck, zContext, "Child page depth differs"); if( i>0 && d2!=depth ){
checkAppendMsg(pCheck, zContext, "Child page depth differs");
}
depth = d2;
} }
depth = d2;
sqliteFree(zKey1);
zKey1 = zKey2;
nKey1 = nKey2;
} }
pgno = SWAB32(pBt, pPage->u.hdr.rightChild); if( !pPage->leaf ){
sprintf(zContext, "On page %d at right child: ", iPage); pgno = get4byte(&pPage->aData[pPage->hdrOffset+6]);
checkTreePage(pCheck, pgno, pPage, zContext, zKey1,nKey1,zUpperBound,nUpper); sprintf(zContext, "On page %d at right child: ", iPage);
sqliteFree(zKey1); checkTreePage(pCheck, pgno, pPage, zContext,0,0,0,0);
}
/* Check for complete coverage of the page /* Check for complete coverage of the page
*/ */
memset(hit, 0, sizeof(hit)); memset(hit, 0, pageSize);
memset(hit, 1, sizeof(PageHdr)); memset(hit, 1, pPage->hdrOffset+10-4*(pPage->leaf));
for(i=SWAB16(pBt, pPage->u.hdr.firstCell); i>0 && i<SQLITE_USABLE_SIZE; ){ data = pPage->aData;
Cell *pCell = (Cell*)&pPage->u.aDisk[i]; hdr = pPage->hdrOffset;
for(cnt=0, i=get2byte(&data[hdr+3]); i>0 && i<pageSize && cnt<10000; cnt++){
int size = cellSize(pPage, &data[i]);
int j; int j;
for(j=i+cellSize(pBt, pCell)-1; j>=i; j--) hit[j]++; for(j=i+size-1; j>=i; j--) hit[j]++;
i = SWAB16(pBt, pCell->h.iNext); i = get2byte(&data[i]);
} }
for(i=SWAB16(pBt,pPage->u.hdr.firstFree); i>0 && i<SQLITE_USABLE_SIZE; ){ for(cnt=0, i=get2byte(&data[hdr+1]); i>0 && i<pageSize && cnt<10000; cnt++){
FreeBlk *pFBlk = (FreeBlk*)&pPage->u.aDisk[i]; int size = get2byte(&data[i+2]);
int j; int j;
for(j=i+SWAB16(pBt,pFBlk->iSize)-1; j>=i; j--) hit[j]++; for(j=i+size-1; j>=i; j--) hit[j]++;
i = SWAB16(pBt,pFBlk->iNext); i = get2byte(&data[i]);
} }
for(i=0; i<SQLITE_USABLE_SIZE; i++){ for(i=cnt=0; i<pageSize; i++){
if( hit[i]==0 ){ if( hit[i]==0 ){
sprintf(zMsg, "Unused space at byte %d of page %d", i, iPage); cnt++;
checkAppendMsg(pCheck, zMsg, 0);
break;
}else if( hit[i]>1 ){ }else if( hit[i]>1 ){
sprintf(zMsg, "Multiple uses for byte %d of page %d", i, iPage); sprintf(zMsg, "Multiple uses for byte %d of page %d", i, iPage);
checkAppendMsg(pCheck, zMsg, 0); checkAppendMsg(pCheck, zMsg, 0);
break; break;
} }
} }
if( cnt!=data[hdr+5] ){
#endif sprintf(zMsg, "Fragmented space is %d byte reported as %d on page %d",
cnt, data[hdr+5], iPage);
checkAppendMsg(pCheck, zMsg, 0);
}
releasePage(pPage); releasePage(pPage);
return depth; return depth+1;
} }
/* /*
@@ -3601,8 +3780,7 @@ char *sqlite3BtreeIntegrityCheck(Btree *pBt, int *aRoot, int nRoot){
return 0; return 0;
} }
sCheck.anRef = sqliteMallocRaw( (sCheck.nPage+1)*sizeof(sCheck.anRef[0]) ); sCheck.anRef = sqliteMallocRaw( (sCheck.nPage+1)*sizeof(sCheck.anRef[0]) );
sCheck.anRef[1] = 1; for(i=0; i<=sCheck.nPage; i++){ sCheck.anRef[i] = 0; }
for(i=2; i<=sCheck.nPage; i++){ sCheck.anRef[i] = 0; }
sCheck.zErrMsg = 0; sCheck.zErrMsg = 0;
/* Check the integrity of the freelist /* Check the integrity of the freelist

View File

@@ -11,7 +11,7 @@
# This file implements regression tests for SQLite library. The # This file implements regression tests for SQLite library. The
# focus of this script is btree database backend # focus of this script is btree database backend
# #
# $Id: btree.test,v 1.22 2004/05/09 01:35:06 drh Exp $ # $Id: btree.test,v 1.23 2004/05/09 11:51:40 drh Exp $
set testdir [file dirname $argv0] set testdir [file dirname $argv0]
@@ -661,7 +661,7 @@ do_test btree-8.8 {
btree_commit $::b1 btree_commit $::b1
btree_data $::c1 btree_data $::c1
} $::data } $::data
do_test btree-8.9 { do_test btree-8.9.1 {
btree_close_cursor $::c1 btree_close_cursor $::c1
btree_close $::b1 btree_close $::b1
set ::b1 [btree_open test1.bt 2000 0] set ::b1 [btree_open test1.bt 2000 0]
@@ -669,6 +669,9 @@ do_test btree-8.9 {
btree_move_to $::c1 2030 btree_move_to $::c1 2030
btree_data $::c1 btree_data $::c1
} $::data } $::data
do_test btree-8.9.2 {
btree_integrity_check $::b1 1 2
} {}
do_test btree-8.10 { do_test btree-8.10 {
btree_begin_transaction $::b1 btree_begin_transaction $::b1
btree_delete $::c1 btree_delete $::c1
@@ -679,14 +682,17 @@ do_test btree-8.11 {
# Now check out keys on overflow pages. # Now check out keys on overflow pages.
# #
do_test btree-8.12 { do_test btree-8.12.1 {
set ::keyprefix "This is a long prefix to a key " set ::keyprefix "This is a long prefix to a key "
while {[string length $::keyprefix]<256} {append ::keyprefix $::keyprefix} while {[string length $::keyprefix]<256} {append ::keyprefix $::keyprefix}
btree_close_cursor $::c1 btree_close_cursor $::c1
btree_clear_table $::b1 2 btree_clear_table $::b1 2
lindex [btree_get_meta $::b1] 0 lindex [btree_get_meta $::b1] 0
} {4} } {4}
do_test btree-8.12.1 { do_test btree-8.12.2 {
btree_integrity_check $::b1 1 2
} {}
do_test btree-8.12.3 {
set ::c1 [btree_cursor $::b1 2 1] set ::c1 [btree_cursor $::b1 2 1]
btree_insert $::c1 ${::keyprefix}1 1 btree_insert $::c1 ${::keyprefix}1 1
btree_first $::c1 btree_first $::c1
@@ -818,13 +824,19 @@ do_test btree-9.6 {
btree_close_cursor $::c1 btree_close_cursor $::c1
lindex [btree_pager_stats $::b1] 1 lindex [btree_pager_stats $::b1] 1
} {1} } {1}
puts "222: [btree_integrity_check $::b1 1 2]"
do_test btree-9.7 { do_test btree-9.7 {
btree_integrity_check $::b1 1 2
} {}
do_test btree-9.8 {
btree_rollback $::b1 btree_rollback $::b1
lindex [btree_pager_stats $::b1] 1 lindex [btree_pager_stats $::b1] 1
} {0} } {0}
do_test btree-9.8 { do_test btree-9.9 {
btree_integrity_check $::b1 1 2
} {}
do_test btree-9.10 {
btree_close $::b1
set ::b1 [btree_open test1.bt 2000 0]
btree_integrity_check $::b1 1 2 btree_integrity_check $::b1 1 2
} {} } {}
@@ -979,8 +991,8 @@ do_test btree-12.12 {
btree_next $::c1 btree_next $::c1
btree_key $::c1 btree_key $::c1
} {402} } {402}
btree_commit $::b1 # btree_commit $::b1
btree_tree_dump $::b1 1 # btree_tree_dump $::b1 1
do_test btree-13.1 { do_test btree-13.1 {
btree_integrity_check $::b1 1 2 btree_integrity_check $::b1 1 2
} {} } {}