diff --git a/manifest b/manifest index 639f6937b2..1062579847 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Begin\strying\sto\sget\sintegrity\schecking\sworking\son\sthe\snew\sbtree.c.\s(CVS\s1329) -D 2004-05-09T01:35:06 +C The\sbtree.test\stest\sis\sno\sworking\swith\sintegrity_check\senabled.\s(CVS\s1330) +D 2004-05-09T11:51:39 F Makefile.in ab7b0d5118e2da97bac66be8684a1034e3500f5a F Makefile.linux-gcc b86a99c493a5bfb402d1d9178dcdc4bd4b32f906 F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd @@ -23,7 +23,7 @@ F sqlite.def fc4f5734786fe4743cfe2aa98eb2da4b089edb5f F sqlite.pc.in 30552343140c53304c2a658c080fbe810cd09ca2 F src/attach.c fa9a58234406d84eeb900517d0c0adc4b2da051a F src/auth.c a2a46e3ed7799134cf3d2dd5ae6650115f26b653 -F src/btree.c 4c9b4a68f5e0c57382fca08d73426ca5af55c80f +F src/btree.c c1251660eaefaf3b11798c8891c90f5412f7ecae F src/btree.h 825034a68947baf99507f04f318f417013dcd3a3 F src/btree_rb.c 47e5b5ec90846af392b5668b34648198ba459561 F src/build.c 21b6645c966970dac51bcccfa8650403a3f56210 @@ -75,7 +75,7 @@ F test/auth.test 5c4d95cdaf539c0c236e20ce1f71a93e7dde9185 F test/bigfile.test ea904b853ce2d703b16c5ce90e2b54951bc1ae81 F test/bigrow.test 8ab252dba108f12ad64e337b0f2ff31a807ac578 F test/bind.test 56a57043b42c4664ca705f6050e56717a8a6699a -F test/btree.test c4399a76bcc6d2c84b156e3a8fef29879d2e54c4 +F test/btree.test ed5781db83b6c1de02e62781d44915a9abe3450a F test/btree2.test 2ff77e0218e5f55ff5a85874f3e15c7859e3ac26 F test/btree3.test e597fb59be2ac0ea69c62aaa2064e998e528b665 F test/btree3rb.test 127efcf5cdfcc352054e7db12622b01cdd8b36ac @@ -190,7 +190,7 @@ F www/sqlite.tcl 3c83b08cf9f18aa2d69453ff441a36c40e431604 F www/tclsqlite.tcl b9271d44dcf147a93c98f8ecf28c927307abd6da F www/vdbe.tcl 9b9095d4495f37697fd1935d10e14c6015e80aa1 F www/whentouse.tcl a8335bce47cc2fddb07f19052cb0cb4d9129a8e4 -P ee706e9c74c3fb32fc3369db226fad9ed4db7596 -R cfbf2d289e390333f8744d3cd9325bcf +P 499569daa6a3aed6609bcb1e11a3d231e13f4f9c +R b0518d68837036ab90f8b4f001412933 U drh -Z d0e25b8c23267f444288dc2922ce4fd0 +Z 029dd36060965161aaa7a2d625e23da4 diff --git a/manifest.uuid b/manifest.uuid index 675091aec8..245f2bd006 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -499569daa6a3aed6609bcb1e11a3d231e13f4f9c \ No newline at end of file +9f1caa530e69aaf202debac36b6a46d707f362d7 \ No newline at end of file diff --git a/src/btree.c b/src/btree.c index c79a6b3d9d..0a23514133 100644 --- a/src/btree.c +++ b/src/btree.c @@ -9,7 +9,7 @@ ** 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. ** For a detailed discussion of BTrees, refer to @@ -218,6 +218,7 @@ struct MemPage { u8 leaf; /* True if leaf flag is set */ u8 zeroData; /* True if zero data flag is set */ 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 nFree; /* Number of free bytes on the page */ int nCell; /* Number of entries on this page */ @@ -377,6 +378,78 @@ static int cellSize(MemPage *pPage, unsigned char *pCell){ 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; ileaf*4; i++) used[i] = 1; + nFree = 0; + pc = get2byte(&data[hdr+1]); + while( pc ){ + int size; + assert( pc>0 && pcisInit==0 || pPage->nFree==nFree+data[hdr+5] ); + idx = 0; + pc = get2byte(&data[hdr+3]); + while( pc ){ + int size; + assert( pPage->isInit==0 || idxnCell ); + assert( pc>0 && pcisInit==0 || pPage->aCell[idx]==&data[pc] ); + size = cellSize(pPage, &data[pc]); + assert( pc+size<=pageSize ); + for(i=pc; inCell ); + nFree = 0; + for(i=0; iaData) ); assert( pPage->pBt!=0 ); assert( pPage->pBt->pageSize <= MX_PAGE_SIZE ); + assert( !pPage->needRelink ); + assert( !pPage->isOverfull ); oldPage = pPage->aData; hdr = pPage->hdrOffset; addr = 3+hdr; @@ -408,9 +483,10 @@ static void defragmentPage(MemPage *pPage){ size = cellSize(pPage, &oldPage[pc]); memcpy(&newPage[n], &oldPage[pc], size); put2byte(&newPage[addr],n); + assert( pPage->aCell[i]==&oldPage[pc] ); pPage->aCell[i++] = &oldPage[n]; + addr = n; n += size; - addr = pc; pc = get2byte(&oldPage[pc]); } assert( i==pPage->nCell ); @@ -599,13 +675,12 @@ static int initPage( assert( pParent==0 || pParent->pBt==pPage->pBt ); assert( pPage->pgno==sqlite3pager_pagenumber(pPage->aData) ); assert( pPage->aData == &((unsigned char*)pPage)[-pPage->pBt->pageSize] ); - assert( pPage->isInit==0 || pPage->pParent==pParent ); - if( pPage->isInit ) return SQLITE_OK; - assert( pPage->pParent==0 ); - pPage->pParent = pParent; - if( pParent ){ + assert( pPage->pParent==0 || pPage->pParent==pParent ); + if( pPage->pParent==0 && pParent!=0 ){ + pPage->pParent = pParent; sqlite3pager_ref(pParent->aData); } + if( pPage->isInit ) return SQLITE_OK; pPage->nCell = pPage->nCellAlloc = 0; assert( pPage->hdrOffset==(pPage->pgno==1 ? 100 : 0) ); hdr = pPage->hdrOffset; @@ -615,6 +690,7 @@ static int initPage( pPage->zeroData = (c & PTF_ZERODATA)!=0; pPage->leaf = (c & PTF_LEAF)!=0; pPage->isOverfull = 0; + pPage->needRelink = 0; pPage->idxShift = 0; pageSize = pPage->pBt->pageSize; @@ -657,6 +733,7 @@ static int initPage( } pPage->isInit = 1; + pageIntegrity(pPage); return SQLITE_OK; } @@ -670,6 +747,8 @@ static void zeroPage(MemPage *pPage, int flags){ int hdr = pPage->hdrOffset; int first; + assert( sqlite3pager_pagenumber(data)==pPage->pgno ); + assert( &data[pBt->pageSize] == (unsigned char*)pPage ); assert( sqlite3pager_iswriteable(data) ); memset(&data[hdr], 0, pBt->pageSize - hdr); data[hdr] = flags; @@ -685,6 +764,11 @@ static void zeroPage(MemPage *pPage, int flags){ pPage->leaf = (flags & PTF_LEAF)!=0; pPage->zeroData = (flags & PTF_ZERODATA)!=0; 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){ MemPage *pPage = (MemPage*)&((char*)pData)[SQLITE_PAGE_SIZE]; + assert( pPage->isInit==0 || pPage->needRelink==0 ); if( pPage->pParent ){ MemPage *pParent = pPage->pParent; pPage->pParent = 0; @@ -996,7 +1081,8 @@ static void invalidateCursors(Btree *pBt){ BtCursor *pCur; for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){ MemPage *pPage = pCur->pPage; - if( pPage && !pPage->isInit ){ + if( pPage /* && !pPage->isInit */ ){ + pageIntegrity(pPage); releasePage(pPage); pCur->pPage = 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 ** invalided by this operation. Any attempt to use a cursor @@ -1267,6 +1371,7 @@ int sqlite3BtreeKeySize(BtCursor *pCur, u64 *pSize){ *pSize = 0; }else{ pPage = pCur->pPage; + pageIntegrity(pPage); assert( pPage!=0 ); assert( pCur->idx>=0 && pCur->idxnCell ); cell = pPage->aCell[pCur->idx]; @@ -1309,6 +1414,7 @@ static int getPayload( assert( pCur->isValid ); pBt = pCur->pBt; pPage = pCur->pPage; + pageIntegrity(pPage); assert( pCur->idx>=0 && pCur->idxnCell ); aPayload = pPage->aCell[pCur->idx]; aPayload += 2; /* Skip the next cell index */ @@ -1431,6 +1537,7 @@ void *sqlite3BtreeKeyFetch(BtCursor *pCur){ assert( pCur->idx>=0 && pCur->idxpPage->nCell ); pBt = pCur->pBt; pPage = pCur->pPage; + pageIntegrity(pPage); assert( pCur->idx>=0 && pCur->idxnCell ); assert( pPage->intKey==0 ); aPayload = pPage->aCell[pCur->idx]; @@ -1467,6 +1574,7 @@ int sqlite3BtreeDataSize(BtCursor *pCur, u32 *pSize){ pPage = pCur->pPage; assert( pPage!=0 ); assert( pPage->isInit ); + pageIntegrity(pPage); if( pPage->zeroData ){ *pSize = 0; }else{ @@ -1516,6 +1624,7 @@ static int moveToChild(BtCursor *pCur, u32 newPgno){ assert( pCur->isValid ); rc = getAndInitPage(pBt, newPgno, &pNewPage, pCur->pPage); if( rc ) return rc; + pageIntegrity(pNewPage); pNewPage->idxParent = pCur->idx; pOldPage = pCur->pPage; pOldPage->idxShift = 0; @@ -1539,8 +1648,9 @@ static int moveToChild(BtCursor *pCur, u32 newPgno){ */ static int isRootPage(MemPage *pPage){ MemPage *pParent = pPage->pParent; - assert( pParent==0 || pParent->isInit ); - if( pParent==0 || (pParent->pgno==1 && pParent->nCell==0) ) return 1; + if( pParent==0 ) return 1; + if( pParent->pgno>1 ) return 0; + if( get2byte(&pParent->aData[pParent->hdrOffset+3])==0 ) return 1; return 0; } @@ -1562,8 +1672,10 @@ static void moveToParent(BtCursor *pCur){ pPage = pCur->pPage; assert( pPage!=0 ); assert( !isRootPage(pPage) ); + pageIntegrity(pPage); pParent = pPage->pParent; assert( pParent!=0 ); + pageIntegrity(pParent); idxParent = pPage->idxParent; sqlite3pager_ref(pParent->aData); oldPgno = pPage->pgno; @@ -1613,6 +1725,7 @@ static int moveToRoot(BtCursor *pCur){ return rc; } releasePage(pCur->pPage); + pageIntegrity(pRoot); pCur->pPage = pRoot; pCur->idx = 0; 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 */ lwr = 0; upr = pPage->nCell-1; + pageIntegrity(pPage); while( lwr<=upr ){ void *pCellKey; u64 nCellKey; @@ -2024,6 +2138,8 @@ static int freePage(MemPage *pPage){ if( n==0 ){ /* This is the first free page */ + rc = sqlite3pager_write(pPage->aData); + if( rc ) return rc; memset(pPage->aData, 0, 8); put4byte(&pPage1->aData[32], pPage->pgno); }else{ @@ -2195,14 +2311,16 @@ static void reparentPage(Btree *pBt, Pgno pgno, MemPage *pNewParent, int idx){ if( pgno==0 ) return; assert( pBt->pPager!=0 ); aData = sqlite3pager_lookup(pBt->pPager, pgno); - pThis = (MemPage*)&aData[pBt->pageSize]; - if( pThis && pThis->isInit ){ - if( pThis->pParent!=pNewParent ){ - if( pThis->pParent ) sqlite3pager_unref(pThis->pParent->aData); - pThis->pParent = pNewParent; - if( pNewParent ) sqlite3pager_ref(pNewParent->aData); + if( aData ){ + pThis = (MemPage*)&aData[pBt->pageSize]; + if( pThis->isInit ){ + if( pThis->pParent!=pNewParent ){ + if( pThis->pParent ) sqlite3pager_unref(pThis->pParent->aData); + pThis->pParent = pNewParent; + if( pNewParent ) sqlite3pager_ref(pNewParent->aData); + } + pThis->idxParent = idx; } - pThis->idxParent = idx; sqlite3pager_unref(aData); } } @@ -2238,25 +2356,45 @@ static void reparentChildPages(MemPage *pPage){ ** ** "sz" must be the number of bytes in the cell. ** -** Do not bother maintaining the integrity of the linked list of Cells. -** Only the pPage->aCell[] array is important. The relinkCellList() -** routine will be called soon after this routine in order to rebuild -** the linked list. +** Try to maintain the integrity of the linked list of cells. But if +** the cell being inserted does not fit on the page, this will not be +** possible. If the linked list is not maintained, then just update +** 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){ int j, pc; + u8 *data; assert( idx>=0 && idxnCell ); assert( sz==cellSize(pPage, pPage->aCell[idx]) ); assert( sqlite3pager_iswriteable(pPage->aData) ); assert( pPage->aCell[idx]>=pPage->aData ); 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 ); freeSpace(pPage, pc, sz); for(j=idx; jnCell-1; j++){ pPage->aCell[j] = pPage->aCell[j+1]; } pPage->nCell--; + if( !pPage->isOverfull && !pPage->needRelink ){ + u8 *pPrev; + if( idx==0 ){ + pPrev = &data[pPage->hdrOffset+3]; + }else{ + pPrev = pPage->aCell[idx-1]; + } + if( idxnCell ){ + pc = Addr(pPage->aCell[idx]) - Addr(data); + }else{ + pc = 0; + } + put2byte(pPrev, pc); + pageIntegrity(pPage); + }else{ + pPage->needRelink = 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 ** and set pPage->isOverfull. ** -** Do not bother maintaining the integrity of the linked list of Cells. -** Only the pPage->aCell[] array is important. The relinkCellList() -** routine will be called soon after this routine in order to rebuild -** the linked list. +** Try to maintain the integrity of the linked list of cells. But if +** the cell being inserted does not fit on the page, this will not be +** possible. If the linked list is not maintained, then just update +** 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){ int idx, j; assert( i>=0 && i<=pPage->nCell ); assert( sz==cellSize(pPage, pCell) ); assert( sqlite3pager_iswriteable(pPage->aData) ); - idx = allocateSpace(pPage, sz); + idx = pPage->needRelink ? 0 : allocateSpace(pPage, sz); resizeCellArray(pPage, pPage->nCell+1); for(j=pPage->nCell; j>i; j--){ 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->aCell[i] = pCell; }else{ - memcpy(&pPage->aData[idx], pCell, sz); - pPage->aCell[i] = &pPage->aData[idx]; + u8 *data = pPage->aData; + 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; } @@ -2303,6 +2458,7 @@ static void insertCell(MemPage *pPage, int i, unsigned char *pCell, int sz){ static void relinkCellList(MemPage *pPage){ int i, idxFrom; assert( sqlite3pager_iswriteable(pPage->aData) ); + if( !pPage->needRelink ) return; idxFrom = pPage->hdrOffset+3; for(i=0; inCell; i++){ int idx = Addr(pPage->aCell[i]) - Addr(pPage->aData); @@ -2311,6 +2467,7 @@ static void relinkCellList(MemPage *pPage){ idxFrom = idx; } 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. */ -static void copyPage(MemPage *pTo, MemPage *pFrom){ +static void movePage(MemPage *pTo, MemPage *pFrom){ uptr from, to; int i; 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 ** 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 pageFlags; /* Value of pPage->aData[0] */ 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 */ Pgno pgnoOld[NB]; /* Page numbers for each page in apOld[] */ 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. */ pParent = pPage->pParent; + TRACE(("BALANCE: begin page %d\n", pPage->pgno)); if( pParent==0 ){ Pgno pgnoChild; MemPage *pChild; @@ -2459,6 +2627,7 @@ static int balance(MemPage *pPage){ if( pPage->leaf ){ /* The table is completely empty */ relinkCellList(pPage); + TRACE(("BALANCE: empty table\n")); }else{ /* The root page is empty but has one child. Transfer the ** 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])); } freePage(pChild); + TRACE(("BALANCE: child %d transfer to page 1\n", pChild->pgno)); }else{ /* The child has more information that will fit on the root. ** The tree is already balanced. Do nothing. */ + TRACE(("BALANCE: child %d will not fit on page 1\n", pChild->pgno)); } }else{ memcpy(pPage, pChild, pBt->pageSize); @@ -2500,6 +2671,7 @@ static int balance(MemPage *pPage){ rc = initPage(pPage, 0); assert( rc==SQLITE_OK ); freePage(pChild); + TRACE(("BALANCE: transfer child %d into root\n", pChild->pgno)); } reparentChildPages(pPage); releasePage(pChild); @@ -2510,6 +2682,7 @@ static int balance(MemPage *pPage){ /* It is OK for the root page to be less than half full. */ relinkCellList(pPage); + TRACE(("BALANCE: Root page is underfull but that is ok\n")); return SQLITE_OK; } /* @@ -2523,17 +2696,18 @@ static int balance(MemPage *pPage){ rc = allocatePage(pBt, &pChild, &pgnoChild, pPage->pgno); if( rc ) return rc; assert( sqlite3pager_iswriteable(pChild->aData) ); - copyPage(pChild, pPage); + movePage(pChild, pPage); assert( pChild->aData[0]==pPage->aData[pPage->hdrOffset] ); pChild->pParent = pPage; - pChild->idxParent = 0; sqlite3pager_ref(pPage->aData); + pChild->idxParent = 0; pChild->isOverfull = 1; zeroPage(pPage, pChild->aData[0] & ~PTF_LEAF); put4byte(&pPage->aData[pPage->hdrOffset+6], pChild->pgno); pParent = pPage; pPage = pChild; - initPage(pParent, 0); + extraUnref = pChild; + TRACE(("BALANCE: Copy root into %d and blance\n", pPage->pgno)); } rc = sqlite3pager_write(pParent->aData); if( rc ) return rc; @@ -2610,7 +2784,9 @@ static int balance(MemPage *pPage){ for(i=0; iaData = &((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++; } if( ileaf ){ assert( leafCorrection==0 ); /* The right pointer of the child page pOld becomes the left @@ -2691,18 +2868,19 @@ static int balance(MemPage *pPage){ assert( pPage->pgno>1 ); pageFlags = pPage->aData[0]; for(i=0; iaData); + sqlite3pager_write(pNew->aData); }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; + apNew[i] = pNew; } nNew++; - zeroPage(apNew[i], pageFlags); - apNew[i]->isInit = 1; + zeroPage(pNew, pageFlags); } /* Free any old pages that were not reused as new pages. @@ -2710,7 +2888,7 @@ static int balance(MemPage *pPage){ while( iaData); + releasePage(apOld[i]); apOld[i] = 0; i++; } @@ -2804,7 +2982,11 @@ static int balance(MemPage *pPage){ /* ** balance the parent page. */ + assert( pPage->isInit ); + assert( pParent->isInit ); + pageIntegrity(pPage); rc = balance(pParent); + /* ** Cleanup before returning. @@ -2813,7 +2995,6 @@ balance_cleanup: for(i=0; ipParent); sqliteFree(apCopy[i]->aCell); } } @@ -2821,6 +3002,8 @@ balance_cleanup: releasePage(apNew[i]); } releasePage(pParent); + releasePage(extraUnref); + TRACE(("BALANCE: Finished with %d\n", pPage->pgno)); return rc; } @@ -2972,6 +3155,7 @@ int sqlite3BtreeDelete(BtCursor *pCur){ unsigned char *pNext; int szNext; int notUsed; + unsigned char tempbuf[4]; getTempCursor(pCur, &leafCur); rc = sqlite3BtreeNext(&leafCur, ¬Used); if( rc!=SQLITE_OK ){ @@ -2983,10 +3167,12 @@ int sqlite3BtreeDelete(BtCursor *pCur){ dropCell(pPage, pCur->idx, cellSize(pPage, pCell)); pNext = leafCur.pPage->aCell[leafCur.idx]; szNext = cellSize(leafCur.pPage, pNext); - insertCell(pPage, pCur->idx, &pNext[-4], szNext+4); + memcpy(tempbuf, &pNext[-2], 4); put4byte(&pNext[-2], pgnoChild); + insertCell(pPage, pCur->idx, &pNext[-4], szNext+4); rc = balance(pPage); if( rc ) return rc; + memcpy(&pNext[-2], tempbuf, 4); dropCell(leafCur.pPage, leafCur.idx, szNext); rc = balance(leafCur.pPage); releaseTempCursor(&leafCur); @@ -3019,7 +3205,7 @@ int sqlite3BtreeCreateTable(Btree *pBt, int *piTable, int flags){ if( pBt->readOnly ){ return SQLITE_READONLY; } - rc = allocatePage(pBt, &pRoot, &pgnoRoot, 0); + rc = allocatePage(pBt, &pRoot, &pgnoRoot, 1); if( rc ) return rc; assert( sqlite3pager_iswriteable(pRoot->aData) ); zeroPage(pRoot, flags | PTF_LEAF); @@ -3190,8 +3376,9 @@ int sqlite3BtreePageDump(Btree *pBt, int pgno, int recursive){ pPage->intKey = (c & PTF_INTKEY)!=0; pPage->zeroData = (c & PTF_ZERODATA)!=0; pPage->leaf = (c & PTF_LEAF)!=0; - printf("PAGE %d: flags=0x%02x frag=%d\n", pgno, - data[hdr], data[hdr+5]); + printf("PAGE %d: flags=0x%02x frag=%d parent=%d\n", pgno, + data[hdr], data[hdr+5], + (pPage->isInit && pPage->pParent) ? pPage->pParent->pgno : 0); i = 0; assert( hdr == (pgno==1 ? 100 : 0) ); idx = get2byte(&data[hdr+3]); @@ -3287,9 +3474,11 @@ int sqlite3BtreeFlags(BtCursor *pCur){ ** ** This routine is used for testing and debugging only. */ -int sqlite3BtreeCursorDump(BtCursor *pCur, int *aResult){ +int sqlite3BtreeCursorInfo(BtCursor *pCur, int *aResult){ int cnt, idx; MemPage *pPage = pCur->pPage; + + pageIntegrity(pPage); assert( pPage->isInit ); aResult[0] = sqlite3pager_pagenumber(pPage->aData); assert( aResult[0]==pPage->pgno ); @@ -3439,9 +3628,9 @@ static int keyCompare( ** ** 1. Make sure that cells and freeblocks do not overlap ** but combine to completely cover the page. -** 2. Make sure cell keys are in order. -** 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 2. Make sure cell keys are in order. +** NO 3. Make sure no key is less than or equal to zLowerBound. +** NO 4. Make sure no key is greater than or equal to zUpperBound. ** 5. Check the integrity of overflow pages. ** 6. Recursively call checkTreePage on all children. ** 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 */ ){ MemPage *pPage; - int i, rc, depth, d2, pgno; + int i, rc, depth, d2, pgno, cnt; + int hdr; + u8 *data; char *zKey1, *zKey2; int nKey1, nKey2; BtCursor cur; Btree *pBt; + int maxLocal, pageSize; char zMsg[100]; char zContext[100]; - char hit[SQLITE_USABLE_SIZE]; + char hit[MX_PAGE_SIZE]; /* Check that the page exists */ cur.pBt = pBt = pCheck->pBt; + maxLocal = pBt->maxLocal; + pageSize = pBt->pageSize; if( iPage==0 ) return 0; if( checkRef(pCheck, iPage, zParentContext) ) return 0; sprintf(zContext, "On tree page %d: ", iPage); @@ -3486,92 +3680,77 @@ static int checkTreePage( return 0; } -#if 0 - /* Check out all the cells. */ 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; inCell; i++){ - Cell *pCell = pPage->aCell[i]; - int sz; + u8 *pCell = pPage->aCell[i]; + u64 nKey, nData; + int sz, nHeader; /* Check payload overflow pages */ - 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); - } - - /* 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"); + parseCellHeader(pPage, pCell, &nData, &nKey, &nHeader); + sz = nData; + if( !pPage->intKey ) sz += nKey; + if( sz>maxLocal ){ + int nPage = (sz - maxLocal + pageSize - 5)/(pageSize - 4); + checkList(pCheck, 0, get4byte(&pCell[nHeader+maxLocal]),nPage,zContext); } /* Check sanity of left child page. */ - 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"); + if( !pPage->leaf ){ + pgno = get4byte(&pCell[2]); + d2 = checkTreePage(pCheck,pgno,pPage,zContext,0,0,0,0); + 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); - sprintf(zContext, "On page %d at right child: ", iPage); - checkTreePage(pCheck, pgno, pPage, zContext, zKey1,nKey1,zUpperBound,nUpper); - sqliteFree(zKey1); + if( !pPage->leaf ){ + pgno = get4byte(&pPage->aData[pPage->hdrOffset+6]); + sprintf(zContext, "On page %d at right child: ", iPage); + checkTreePage(pCheck, pgno, pPage, zContext,0,0,0,0); + } /* Check for complete coverage of the page */ - memset(hit, 0, sizeof(hit)); - memset(hit, 1, sizeof(PageHdr)); - for(i=SWAB16(pBt, pPage->u.hdr.firstCell); i>0 && iu.aDisk[i]; + memset(hit, 0, pageSize); + memset(hit, 1, pPage->hdrOffset+10-4*(pPage->leaf)); + data = pPage->aData; + hdr = pPage->hdrOffset; + for(cnt=0, i=get2byte(&data[hdr+3]); i>0 && i=i; j--) hit[j]++; - i = SWAB16(pBt, pCell->h.iNext); + for(j=i+size-1; j>=i; j--) hit[j]++; + i = get2byte(&data[i]); } - for(i=SWAB16(pBt,pPage->u.hdr.firstFree); i>0 && iu.aDisk[i]; + for(cnt=0, i=get2byte(&data[hdr+1]); i>0 && iiSize)-1; j>=i; j--) hit[j]++; - i = SWAB16(pBt,pFBlk->iNext); + for(j=i+size-1; j>=i; j--) hit[j]++; + i = get2byte(&data[i]); } - for(i=0; i1 ){ sprintf(zMsg, "Multiple uses for byte %d of page %d", i, iPage); checkAppendMsg(pCheck, zMsg, 0); break; } } - -#endif + if( cnt!=data[hdr+5] ){ + sprintf(zMsg, "Fragmented space is %d byte reported as %d on page %d", + cnt, data[hdr+5], iPage); + checkAppendMsg(pCheck, zMsg, 0); + } releasePage(pPage); - return depth; + return depth+1; } /* @@ -3601,8 +3780,7 @@ char *sqlite3BtreeIntegrityCheck(Btree *pBt, int *aRoot, int nRoot){ 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; } + for(i=0; i<=sCheck.nPage; i++){ sCheck.anRef[i] = 0; } sCheck.zErrMsg = 0; /* Check the integrity of the freelist diff --git a/test/btree.test b/test/btree.test index 184602073d..b0a747bff4 100644 --- a/test/btree.test +++ b/test/btree.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # 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] @@ -661,7 +661,7 @@ do_test btree-8.8 { btree_commit $::b1 btree_data $::c1 } $::data -do_test btree-8.9 { +do_test btree-8.9.1 { btree_close_cursor $::c1 btree_close $::b1 set ::b1 [btree_open test1.bt 2000 0] @@ -669,6 +669,9 @@ do_test btree-8.9 { btree_move_to $::c1 2030 btree_data $::c1 } $::data +do_test btree-8.9.2 { + btree_integrity_check $::b1 1 2 +} {} do_test btree-8.10 { btree_begin_transaction $::b1 btree_delete $::c1 @@ -679,14 +682,17 @@ do_test btree-8.11 { # 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 " while {[string length $::keyprefix]<256} {append ::keyprefix $::keyprefix} btree_close_cursor $::c1 btree_clear_table $::b1 2 lindex [btree_get_meta $::b1] 0 } {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] btree_insert $::c1 ${::keyprefix}1 1 btree_first $::c1 @@ -818,13 +824,19 @@ do_test btree-9.6 { btree_close_cursor $::c1 lindex [btree_pager_stats $::b1] 1 } {1} -puts "222: [btree_integrity_check $::b1 1 2]" - do_test btree-9.7 { + btree_integrity_check $::b1 1 2 +} {} +do_test btree-9.8 { btree_rollback $::b1 lindex [btree_pager_stats $::b1] 1 } {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 } {} @@ -979,8 +991,8 @@ do_test btree-12.12 { btree_next $::c1 btree_key $::c1 } {402} -btree_commit $::b1 -btree_tree_dump $::b1 1 +# btree_commit $::b1 +# btree_tree_dump $::b1 1 do_test btree-13.1 { btree_integrity_check $::b1 1 2 } {}