diff --git a/manifest b/manifest index 9f34ea149f..dfe85bc727 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C The\sbtree.c\smodule\scompiles\sand\slinks\sand\spasses\ssome\stests.\s\sMany\stests\nstill\sfail,\sthough.\s(CVS\s1321) -D 2004-05-07T17:57:50 +C More\sbug\sfixes\sin\sbtree.c.\s(CVS\s1322) +D 2004-05-07T23:50:57 F Makefile.in ab7b0d5118e2da97bac66be8684a1034e3500f5a F Makefile.linux-gcc b86a99c493a5bfb402d1d9178dcdc4bd4b32f906 F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd @@ -23,8 +23,8 @@ F sqlite.def fc4f5734786fe4743cfe2aa98eb2da4b089edb5f F sqlite.pc.in 30552343140c53304c2a658c080fbe810cd09ca2 F src/attach.c b01db0d3211f673d8e670abf7eaad04591d40d14 F src/auth.c 4fa3b05bd19445d1c474d6751c4a508d6ea0abe1 -F src/btree.c ff2f51fcd01c4fb9f2ce0f061a549e0c9aae9d74 -F src/btree.h 49b255b2880c50a3572a536ea935edb2dd33aae3 +F src/btree.c a5fafa6179c80ca422fea96b4553525648f5535e +F src/btree.h ba5d3bfadc3f46f86df525ac07274dc497af856a F src/btree_rb.c 99feb3ff835106d018a483a1ce403e5cf9c718bc F src/build.c 76fbca30081decd6615dee34b48c927ed5063752 F src/copy.c 750e13828c3e4a293123e36aaa7cf0f22466248a @@ -54,7 +54,7 @@ F src/table.c d845cb101b5afc1f7fea083c99e3d2fa7998d895 F src/tclsqlite.c 9fe6fc0c20820e9411dfea407635de9b9d3ae0e3 F src/test1.c 9aa62b89d420e6763b5e7ae89a47f6cf87370477 F src/test2.c 9d611c45e1b07039a2bd95f5ea73178362b23229 -F src/test3.c d6af4e26bdbb7512ab5d01e49f34e72415d58a3b +F src/test3.c cec7eee9e64f95a3d2e4a3c242e081f560f98cf8 F src/test4.c 6e3e31acfaf21d66420fc35fda5b17dc0000cc8d F src/test5.c 3ff0565057b8d23e20092d5c6c0b7cb0d932c51e F src/tokenize.c 6676b946fd8825b67ab52140af4fdc57a70bda48 @@ -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 77f93efac02dd05c7532b366b226bfe74757af57 +F test/btree.test c8a548edf00f46ceb5b9db8bd5c250e95b744c48 F test/btree2.test e3b81ec33dc2f89b3e6087436dfe605b870c9080 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 dcd6b55f932a7ade4ad058534651e198b56370ad -R cdb403059e0209ca0544e8ccaa4370da +P d394b2b217d4d728f9eba397262bf9d36195719e +R 04bebd7ff9ebbf1a875d441cf21d682e U drh -Z 48eb767d5af5b72432055086442dfd47 +Z ae2d2cf62d0e5010d2f2e28dfebc946c diff --git a/manifest.uuid b/manifest.uuid index 474a8b183d..16e5216723 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -d394b2b217d4d728f9eba397262bf9d36195719e \ No newline at end of file +a80939ef714ec884950b4a1f4f809ffa37fdfa59 \ No newline at end of file diff --git a/src/btree.c b/src/btree.c index 52da31bb56..100630179d 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.111 2004/05/07 17:57:50 drh Exp $ +** $Id: btree.c,v 1.112 2004/05/07 23:50:57 drh Exp $ ** ** This file implements a external (disk-based) database using BTrees. ** For a detailed discussion of BTrees, refer to @@ -266,18 +266,11 @@ struct BtCursor { MemPage *pPage; /* Page that contains the entry */ int idx; /* Index of the entry in pPage->aCell[] */ u8 wrFlag; /* True if writable */ - u8 eSkip; /* Determines if next step operation is a no-op */ u8 iMatch; /* compare result from last sqlite3BtreeMoveto() */ + u8 isValid; /* TRUE if points to a valid entry */ + u8 status; /* Set to SQLITE_ABORT if cursors is invalidated */ }; -/* -** Legal values for BtCursor.eSkip. -*/ -#define SKIP_NONE 0 /* Always step the cursor */ -#define SKIP_NEXT 1 /* The next sqlite3BtreeNext() is a no-op */ -#define SKIP_PREV 2 /* The next sqlite3BtreePrevious() is a no-op */ -#define SKIP_INVALID 3 /* Calls to Next() and Previous() are invalid */ - /* ** Read or write a two-, four-, and eight-byte big-endian integer values. */ @@ -405,6 +398,7 @@ static void defragmentPage(MemPage *pPage){ if( !pPage->leaf ){ n += 4; } + memcpy(&newPage[hdr], &oldPage[hdr], n-hdr); start = n; pc = get2byte(&oldPage[addr]); i = 0; @@ -413,11 +407,12 @@ static void defragmentPage(MemPage *pPage){ size = cellSize(pPage, &oldPage[pc]); memcpy(&newPage[n], &oldPage[pc], size); put2byte(&newPage[addr],n); - pPage->aCell[i] = &oldPage[n]; + pPage->aCell[i++] = &oldPage[n]; n += size; addr = pc; pc = get2byte(&oldPage[pc]); } + assert( i==pPage->nCell ); leftover = pPage->pBt->pageSize - n; assert( leftover>=0 ); assert( pPage->nFree==leftover ); @@ -426,15 +421,16 @@ static void defragmentPage(MemPage *pPage){ leftover = 0; n = pPage->pBt->pageSize; } - memcpy(&oldPage[start], &newPage[start], n-start); + memcpy(&oldPage[hdr], &newPage[hdr], n-hdr); if( leftover==0 ){ - put2byte(&oldPage[hdr+3], 0); + put2byte(&oldPage[hdr+1], 0); }else if( leftover>=4 ){ - put2byte(&oldPage[hdr+3], n); + put2byte(&oldPage[hdr+1], n); put2byte(&oldPage[n], 0); put2byte(&oldPage[n+2], leftover); memset(&oldPage[n+4], 0, leftover-4); } + oldPage[hdr+5] = 0; } /* @@ -1082,6 +1078,22 @@ int sqlite3BtreeCommit(Btree *pBt){ return rc; } +/* +** Invalidate all cursors +*/ +static void invalidateCursors(Btree *pBt){ + BtCursor *pCur; + for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){ + MemPage *pPage = pCur->pPage; + if( pPage && !pPage->isInit ){ + releasePage(pPage); + pCur->pPage = 0; + pCur->isValid = 0; + pCur->status = SQLITE_ABORT; + } + } +} + /* ** Rollback the transaction in progress. All cursors will be ** invalided by this operation. Any attempt to use a cursor @@ -1093,18 +1105,11 @@ int sqlite3BtreeCommit(Btree *pBt){ */ int sqlite3BtreeRollback(Btree *pBt){ int rc; - BtCursor *pCur; if( pBt->inTrans==0 ) return SQLITE_OK; pBt->inTrans = 0; pBt->inStmt = 0; rc = pBt->readOnly ? SQLITE_OK : sqlite3pager_rollback(pBt->pPager); - for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){ - MemPage *pPage = pCur->pPage; - if( pPage && !pPage->isInit ){ - releasePage(pPage); - pCur->pPage = 0; - } - } + invalidateCursors(pBt); unlockBtreeIfUnused(pBt); return rc; } @@ -1155,16 +1160,9 @@ int sqlite3BtreeCommitStmt(Btree *pBt){ */ int sqlite3BtreeRollbackStmt(Btree *pBt){ int rc; - BtCursor *pCur; if( pBt->inStmt==0 || pBt->readOnly ) return SQLITE_OK; rc = sqlite3pager_stmt_rollback(pBt->pPager); - for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){ - MemPage *pPage = pCur->pPage; - if( pPage && !pPage->isInit ){ - releasePage(pPage); - pCur->pPage = 0; - } - } + invalidateCursors(pBt); pBt->inStmt = 0; return rc; } @@ -1265,7 +1263,6 @@ int sqlite3BtreeCursor( pCur->pBt = pBt; pCur->wrFlag = wrFlag; pCur->idx = 0; - pCur->eSkip = SKIP_INVALID; pCur->pNext = pBt->pCursor; if( pCur->pNext ){ pCur->pNext->pPrev = pCur; @@ -1280,6 +1277,8 @@ int sqlite3BtreeCursor( pCur->pShared = pCur; } pBt->pCursor = pCur; + pCur->isValid = 0; + pCur->status = SQLITE_OK; *ppCur = pCur; return SQLITE_OK; @@ -1351,13 +1350,15 @@ static void releaseTempCursor(BtCursor *pCur){ */ int sqlite3BtreeKeySize(BtCursor *pCur, u64 *pSize){ MemPage *pPage; + unsigned char *cell; - pPage = pCur->pPage; - assert( pPage!=0 ); - if( pCur->idx >= pPage->nCell ){ + if( !pCur->isValid ){ *pSize = 0; }else{ - unsigned char *cell = pPage->aCell[pCur->idx]; + pPage = pCur->pPage; + assert( pPage!=0 ); + assert( pCur->idx>=0 && pCur->idxnCell ); + cell = pPage->aCell[pCur->idx]; cell += 2; /* Skip the offset to the next cell */ if( !pPage->leaf ){ cell += 4; /* Skip the child pointer */ @@ -1394,6 +1395,7 @@ static int getPayload( int maxLocal, ovflSize; assert( pCur!=0 && pCur->pPage!=0 ); + assert( pCur->isValid ); pBt = pCur->pBt; pPage = pCur->pPage; assert( pCur->idx>=0 && pCur->idxnCell ); @@ -1474,16 +1476,14 @@ static int getPayload( ** the available payload. */ int sqlite3BtreeKey(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){ - MemPage *pPage; - assert( amt>=0 ); assert( offset>=0 ); - assert( pCur->pPage!=0 ); - pPage = pCur->pPage; - if( pCur->idx >= pPage->nCell || pPage->intKey ){ - assert( amt==0 ); - return SQLITE_OK; + if( pCur->isValid==0 ){ + return pCur->status; } + assert( pCur->pPage!=0 ); + assert( pCur->pPage->intKey==0 ); + assert( pCur->idx>=0 && pCur->idxpPage->nCell ); return getPayload(pCur, offset, amt, (unsigned char*)pBuf, 0); } @@ -1512,10 +1512,16 @@ void *sqlite3BtreeKeyFetch(BtCursor *pCur){ Btree *pBt; u64 nData, nKey; - assert( pCur!=0 && pCur->pPage!=0 ); + assert( pCur!=0 ); + if( !pCur->isValid ){ + return 0; + } + assert( pCur->pPage!=0 ); + assert( pCur->idx>=0 && pCur->idxpPage->nCell ); pBt = pCur->pBt; pPage = pCur->pPage; assert( pCur->idx>=0 && pCur->idxnCell ); + assert( pPage->intKey==0 ); aPayload = pPage->aCell[pCur->idx]; aPayload += 2; /* Skip the next cell index */ if( !pPage->leaf ){ @@ -1525,7 +1531,7 @@ void *sqlite3BtreeKeyFetch(BtCursor *pCur){ aPayload += getVarint(aPayload, &nData); } aPayload += getVarint(aPayload, &nKey); - if( pPage->intKey || nKey>pBt->maxLocal ){ + if( nKey>pBt->maxLocal ){ return 0; } return aPayload; @@ -1541,14 +1547,19 @@ void *sqlite3BtreeKeyFetch(BtCursor *pCur){ */ int sqlite3BtreeDataSize(BtCursor *pCur, u32 *pSize){ MemPage *pPage; + unsigned char *cell; + u64 size; + if( !pCur->isValid ){ + return pCur->status ? pCur->status : SQLITE_INTERNAL; + } pPage = pCur->pPage; assert( pPage!=0 ); - if( pCur->idx >= pPage->nCell || pPage->zeroData ){ + assert( pPage->isInit ); + if( pPage->zeroData ){ *pSize = 0; }else{ - unsigned char *cell; - u64 size; + assert( pCur->idx>=0 && pCur->idxnCell ); cell = pPage->aCell[pCur->idx]; cell += 2; /* Skip the offset to the next cell */ if( !pPage->leaf ){ @@ -1571,15 +1582,13 @@ int sqlite3BtreeDataSize(BtCursor *pCur, u32 *pSize){ ** the available payload. */ int sqlite3BtreeData(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){ - MemPage *pPage; - + if( !pCur->isValid ){ + return pCur->status ? pCur->status : SQLITE_INTERNAL; + } assert( amt>=0 ); assert( offset>=0 ); assert( pCur->pPage!=0 ); - pPage = pCur->pPage; - if( pCur->idx >= pPage->nCell ){ - return 0; - } + assert( pCur->idx>=0 && pCur->idxpPage->nCell ); return getPayload(pCur, offset, amt, pBuf, 1); } @@ -1593,6 +1602,7 @@ static int moveToChild(BtCursor *pCur, u32 newPgno){ MemPage *pOldPage; Btree *pBt = pCur->pBt; + assert( pCur->isValid ); rc = getAndInitPage(pBt, newPgno, &pNewPage, pCur->pPage); if( rc ) return rc; pNewPage->idxParent = pCur->idx; @@ -1637,6 +1647,7 @@ static void moveToParent(BtCursor *pCur){ MemPage *pPage; int idxParent; + assert( pCur->isValid ); pPage = pCur->pPage; assert( pPage!=0 ); assert( !isRootPage(pPage) ); @@ -1686,7 +1697,10 @@ static int moveToRoot(BtCursor *pCur){ Btree *pBt = pCur->pBt; rc = getAndInitPage(pBt, pCur->pgnoRoot, &pRoot, 0); - if( rc ) return rc; + if( rc ){ + pCur->isValid = 0; + return rc; + } releasePage(pCur->pPage); pCur->pPage = pRoot; pCur->idx = 0; @@ -1697,6 +1711,7 @@ static int moveToRoot(BtCursor *pCur){ assert( subpage>0 ); rc = moveToChild(pCur, subpage); } + pCur->isValid = pCur->pPage->nCell>0; return rc; } @@ -1709,6 +1724,7 @@ static int moveToLeftmost(BtCursor *pCur){ int rc; MemPage *pPage; + assert( pCur->isValid ); while( !(pPage = pCur->pPage)->leaf ){ assert( pCur->idx>=0 && pCur->idxnCell ); pgno = get4byte(&pPage->aCell[pCur->idx][2]); @@ -1730,6 +1746,7 @@ static int moveToRightmost(BtCursor *pCur){ int rc; MemPage *pPage; + assert( pCur->isValid ); while( !(pPage = pCur->pPage)->leaf ){ pgno = get4byte(&pPage->aData[pPage->hdrOffset+6]); pCur->idx = pPage->nCell; @@ -1746,16 +1763,19 @@ static int moveToRightmost(BtCursor *pCur){ */ int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){ int rc; - if( pCur->pPage==0 ) return SQLITE_ABORT; + if( pCur->status ){ + return pCur->status; + } rc = moveToRoot(pCur); if( rc ) return rc; - if( pCur->pPage->nCell==0 ){ + if( pCur->isValid==0 ){ + assert( pCur->pPage->nCell==0 ); *pRes = 1; return SQLITE_OK; } + assert( pCur->pPage->nCell>0 ); *pRes = 0; rc = moveToLeftmost(pCur); - pCur->eSkip = SKIP_NONE; return rc; } @@ -1765,17 +1785,19 @@ int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){ */ int sqlite3BtreeLast(BtCursor *pCur, int *pRes){ int rc; - if( pCur->pPage==0 ) return SQLITE_ABORT; + if( pCur->status ){ + return pCur->status; + } rc = moveToRoot(pCur); if( rc ) return rc; - assert( pCur->pPage->isInit ); - if( pCur->pPage->nCell==0 ){ + if( pCur->isValid==0 ){ + assert( pCur->pPage->nCell==0 ); *pRes = 1; return SQLITE_OK; } + assert( pCur->isValid ); *pRes = 0; rc = moveToRightmost(pCur); - pCur->eSkip = SKIP_NONE; return rc; } @@ -1809,10 +1831,18 @@ int sqlite3BtreeLast(BtCursor *pCur, int *pRes){ */ int sqlite3BtreeMoveto(BtCursor *pCur, const void *pKey, u64 nKey, int *pRes){ int rc; - if( pCur->pPage==0 ) return SQLITE_ABORT; - pCur->eSkip = SKIP_NONE; + + if( pCur->status ){ + return pCur->status; + } rc = moveToRoot(pCur); if( rc ) return rc; + assert( pCur->pPage ); + assert( pCur->pPage->isInit ); + if( pCur->isValid==0 ){ + assert( pCur->pPage->nCell==0 ); + return SQLITE_OK; + } for(;;){ int lwr, upr; Pgno chldPg; @@ -1865,16 +1895,30 @@ int sqlite3BtreeMoveto(BtCursor *pCur, const void *pKey, u64 nKey, int *pRes){ } if( chldPg==0 ){ pCur->iMatch = c; + assert( pCur->idx>=0 && pCur->idxpPage->nCell ); if( pRes ) *pRes = c; return SQLITE_OK; } pCur->idx = lwr; rc = moveToChild(pCur, chldPg); - if( rc ) return rc; + if( rc ){ + return rc; + } } /* NOT REACHED */ } +/* +** Return TRUE if the cursor is not pointing at an entry of the table. +** +** TRUE will be returned after a call to sqlite3BtreeNext() moves +** past the last entry in the table or sqlite3BtreePrev() moves past +** the first entry. TRUE is also returned if the table is empty. +*/ +int sqlite3BtreeEof(BtCursor *pCur){ + return pCur->isValid==0; +} + /* ** Advance the cursor to the next entry in the database. If ** successful then set *pRes=0. If the cursor @@ -1885,23 +1929,12 @@ int sqlite3BtreeNext(BtCursor *pCur, int *pRes){ int rc; MemPage *pPage = pCur->pPage; assert( pRes!=0 ); - if( pPage==0 ){ + if( pCur->isValid==0 ){ *pRes = 1; - return SQLITE_ABORT; + return SQLITE_OK; } assert( pPage->isInit ); - assert( pCur->eSkip!=SKIP_INVALID ); - if( pPage->nCell==0 ){ - *pRes = 1; - return SQLITE_OK; - } assert( pCur->idxnCell ); - 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->leaf ){ @@ -1914,6 +1947,7 @@ int sqlite3BtreeNext(BtCursor *pCur, int *pRes){ do{ if( isRootPage(pPage) ){ *pRes = 1; + pCur->isValid = 0; return SQLITE_OK; } moveToParent(pCur); @@ -1940,23 +1974,12 @@ int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){ int rc; Pgno pgno; MemPage *pPage; + if( pCur->isValid==0 ){ + *pRes = 1; + return SQLITE_OK; + } 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( !pPage->leaf ){ pgno = get4byte(&pPage->aCell[pCur->idx][2]); @@ -1966,7 +1989,8 @@ int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){ }else{ while( pCur->idx==0 ){ if( isRootPage(pPage) ){ - if( pRes ) *pRes = 1; + pCur->isValid = 0; + *pRes = 1; return SQLITE_OK; } moveToParent(pCur); @@ -2924,8 +2948,8 @@ int sqlite3BtreeInsert( unsigned char *oldCell; unsigned char newCell[MX_CELL_SIZE]; - if( pCur->pPage==0 ){ - return SQLITE_ABORT; /* A rollback destroyed this cursor */ + if( pCur->status ){ + return pCur->status; /* A rollback destroyed this cursor */ } if( !pBt->inTrans || nKey+nData==0 ){ /* Must start a transaction before doing an insert */ @@ -2969,7 +2993,6 @@ int sqlite3BtreeInsert( /* sqlite3BtreePageDump(pCur->pBt, pCur->pgnoRoot, 1); */ /* fflush(stdout); */ moveToRoot(pCur); - pCur->eSkip = SKIP_INVALID; return rc; } @@ -2985,8 +3008,8 @@ int sqlite3BtreeDelete(BtCursor *pCur){ Btree *pBt = pCur->pBt; assert( pPage->isInit ); - if( pCur->pPage==0 ){ - return SQLITE_ABORT; /* A rollback destroyed this cursor */ + if( pCur->status ){ + return pCur->status; /* A rollback destroyed this cursor */ } if( !pBt->inTrans ){ /* Must start a transaction before doing a delete */ @@ -3276,7 +3299,7 @@ int sqlite3BtreePageDump(Btree *pBt, int pgno, int recursive){ nFree = 0; i = 0; idx = get2byte(&pPage->aData[hdrOffset+1]); - while( idx>0 && idx0 && idxpBt->pageSize ){ int sz = get2byte(&pPage->aData[idx+2]); sprintf(range,"%d..%d", idx, idx+sz-1); nFree += sz; @@ -3346,7 +3369,7 @@ int sqlite3BtreeCursorDump(BtCursor *pCur, int *aResult){ aResult[4] = pPage->nFree; cnt = 0; idx = get2byte(&pPage->aData[pPage->hdrOffset+1]); - while( idx>0 && idx0 && idxpBt->pageSize ){ cnt++; idx = get2byte(&pPage->aData[idx]); } @@ -3707,7 +3730,7 @@ int sqlite3BtreeCopyFile(Btree *pBtTo, Btree *pBtFrom){ if( !pBtTo->inTrans || !pBtFrom->inTrans ) return SQLITE_ERROR; if( pBtTo->pCursor ) return SQLITE_BUSY; - memcpy(pBtTo->pPage1, pBtFrom->pPage1, SQLITE_USABLE_SIZE); + memcpy(pBtTo->pPage1, pBtFrom->pPage1, pBtFrom->pageSize); rc = sqlite3pager_overwrite(pBtTo->pPager, 1, pBtFrom->pPage1); nToPage = sqlite3pager_pagecount(pBtTo->pPager); nPage = sqlite3pager_pagecount(pBtFrom->pPager); diff --git a/src/btree.h b/src/btree.h index 6d8caa1df7..a8d673f716 100644 --- a/src/btree.h +++ b/src/btree.h @@ -13,7 +13,7 @@ ** subsystem. See comments in the source code for a detailed description ** of what each interface routine does. ** -** @(#) $Id: btree.h,v 1.38 2004/05/07 13:30:42 drh Exp $ +** @(#) $Id: btree.h,v 1.39 2004/05/07 23:50:57 drh Exp $ */ #ifndef _BTREE_H_ #define _BTREE_H_ @@ -72,6 +72,7 @@ int sqlite3BtreeInsert(BtCursor*, const void *pKey, u64 nKey, int sqlite3BtreeFirst(BtCursor*, int *pRes); int sqlite3BtreeLast(BtCursor*, int *pRes); int sqlite3BtreeNext(BtCursor*, int *pRes); +int sqlite3BtreeEof(BtCursor*); int sqlite3BtreePrevious(BtCursor*, int *pRes); int sqlite3BtreeKeySize(BtCursor*, u64 *pSize); int sqlite3BtreeKey(BtCursor*, u32 offset, u32 amt, void*); diff --git a/src/test3.c b/src/test3.c index e5a3f1b9ea..bc66659fcd 100644 --- a/src/test3.c +++ b/src/test3.c @@ -13,7 +13,7 @@ ** is not included in the SQLite library. It is used for automated ** testing of the SQLite library. ** -** $Id: test3.c,v 1.27 2004/05/07 17:57:50 drh Exp $ +** $Id: test3.c,v 1.28 2004/05/07 23:50:57 drh Exp $ */ #include "sqliteInt.h" #include "pager.h" @@ -810,6 +810,32 @@ static int btree_last( return SQLITE_OK; } +/* +** Usage: btree_eof ID +** +** Return TRUE if the given cursor is not pointing at a valid entry. +** Return FALSE if the cursor does point to a valid entry. +*/ +static int btree_eof( + void *NotUsed, + Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ + int argc, /* Number of arguments */ + const char **argv /* Text of each argument */ +){ + BtCursor *pCur; + char zBuf[50]; + + if( argc!=2 ){ + Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], + " ID\"", 0); + return TCL_ERROR; + } + if( Tcl_GetInt(interp, argv[1], (int*)&pCur) ) return TCL_ERROR; + sprintf(zBuf, "%d", sqlite3BtreeEof(pCur)); + Tcl_AppendResult(interp, zBuf, 0); + return SQLITE_OK; +} + /* ** Usage: btree_keysize ID ** @@ -1022,6 +1048,7 @@ int Sqlitetest3_Init(Tcl_Interp *interp){ { "btree_insert", (Tcl_CmdProc*)btree_insert }, { "btree_next", (Tcl_CmdProc*)btree_next }, { "btree_prev", (Tcl_CmdProc*)btree_prev }, + { "btree_eof", (Tcl_CmdProc*)btree_eof }, { "btree_keysize", (Tcl_CmdProc*)btree_keysize }, { "btree_key", (Tcl_CmdProc*)btree_key }, { "btree_data", (Tcl_CmdProc*)btree_data }, diff --git a/test/btree.test b/test/btree.test index 9c417ecbe0..71326a53db 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.16 2004/05/07 17:57:50 drh Exp $ +# $Id: btree.test,v 1.17 2004/05/07 23:50:58 drh Exp $ set testdir [file dirname $argv0] @@ -185,13 +185,17 @@ do_test btree-3.18 { do_test btree-3.19 { btree_data $::c1 } {6.00} -do_test btree-3.20 { +do_test btree-3.20.1 { btree_next $::c1 btree_key $::c1 } {0} +do_test btree-3.20.2 { + btree_eof $::c1 +} {1} do_test btree-3.21 { - btree_data $::c1 -} {} + set rc [catch {btree_data $::c1} res] + lappend rc $res +} {1 SQLITE_INTERNAL} # Commit the changes, reopen and reread the data # @@ -270,8 +274,9 @@ do_test btree-3.39 { btree_key $::c1 } {0} do_test btree-3.40 { - btree_data $::c1 -} {} + set rc [catch {btree_data $::c1} res] + lappend rc $res +} {1 SQLITE_INTERNAL} do_test btree-3.41 { lindex [btree_pager_stats $::b1] 1 } {1} @@ -307,7 +312,7 @@ do_test btree-4.4 { set r {} while 1 { set key [btree_key $::c1] - if {$key==0} break + if {[btree_eof $::c1]} break lappend r $key lappend r [btree_data $::c1] btree_next $::c1 @@ -323,7 +328,7 @@ do_test btree-4.5 { set r {} while 1 { set key [btree_key $::c1] - if {$key==0} break + if {[btree_eof $::c1]} break lappend r $key lappend r [btree_data $::c1] btree_next $::c1 @@ -352,7 +357,7 @@ do_test btree-4.9 { btree_first $::c1 while 1 { set key [btree_key $::c1] - if {$key==0} break + if {[btree_eof $::c1]} break lappend r $key lappend r [btree_data $::c1] btree_next $::c1 @@ -396,22 +401,9 @@ do_test btree-5.6 { proc select_all {cursor} { set r {} - btree_move_to $cursor {} - while 1 { + btree_first $cursor + while {![btree_eof $cursor]} { set key [btree_key $cursor] - if {$key==""} break - lappend r $key - lappend r [btree_data $cursor] - btree_next $cursor - } - return $r -} -proc select_all_intkey {cursor} { - set r {} - btree_move_to $cursor 0 - while 1 { - set key [btree_key $cursor] - if {$key==0} break lappend r $key lappend r [btree_data $cursor] btree_next $cursor @@ -420,10 +412,9 @@ proc select_all_intkey {cursor} { } proc select_keys {cursor} { set r {} - btree_move_to $cursor {} - while 1 { + btree_first $cursor + while {![btree_eof $cursor]} { set key [btree_key $cursor] - if {$key==""} break lappend r $key btree_next $cursor } @@ -449,6 +440,7 @@ do_test btree-6.2.2 { } {2} do_test btree-6.2.3 { btree_insert $::c2 ten 10 + btree_move_to $::c2 ten btree_key $::c2 } {ten} do_test btree-6.3 { @@ -457,7 +449,7 @@ do_test btree-6.3 { lindex [btree_pager_stats $::b1] 1 } {2} do_test btree-6.3.1 { - select_all_intkey $::c1 + select_all $::c1 } {200 2.00 300 3.00 400 4.00 500 5.00 600 6.00} #btree_page_dump $::b1 3 do_test btree-6.4 { @@ -497,15 +489,15 @@ do_test btree-6.9.1 { btree_key $::c2 } {} -# If we drop table 2 it just clears the table. Table 2 always exists. +# If we drop table 1 it just clears the table. Table 1 always exists. # do_test btree-6.10 { btree_close_cursor $::c1 - btree_drop_table $::b1 2 - set ::c1 [btree_cursor $::b1 2 1] - btree_move_to $::c1 {} - btree_key $::c1 -} {} + btree_drop_table $::b1 1 + set ::c1 [btree_cursor $::b1 1 1] + btree_first $::c1 + btree_eof $::c1 +} {1} do_test btree-6.11 { btree_commit $::b1 select_all $::c1 @@ -516,12 +508,12 @@ do_test btree-6.12 { do_test btree-6.13 { btree_close_cursor $::c2 lindex [btree_pager_stats $::b1] 1 -} {2} +} {1} # Check to see that pages defragment properly. To do this test we will # -# 1. Fill the first page table 2 with data. -# 2. Delete every other entry of table 2. +# 1. Fill the first page of table 1 with data. +# 2. Delete every other entry of table 1. # 3. Insert a single entry that requires more contiguous # space than is available. # @@ -531,98 +523,112 @@ do_test btree-7.1 { catch {unset key} catch {unset data} do_test btree-7.2 { - for {set i 0} {$i<36} {incr i} { - set key [format %03d $i] - set data "*** $key ***" + # Each record will be 10 bytes in size. + # + 100 bytes of database header + # + 6 bytes of table header + # + 91*10=910 bytes of cells + # Totals 1016 bytes. 8 bytes left over + # Keys are 1000 through 1090. + for {set i 1000} {$i<1091} {incr i} { + set key $i + set data [format %5d $i] btree_insert $::c1 $key $data } lrange [btree_cursor_dump $::c1] 4 5 } {8 1} do_test btree-7.3 { - btree_move_to $::c1 000 - while {[btree_key $::c1]!=""} { + for {set i 1001} {$i<1091} {incr i 2} { + btree_move_to $::c1 $i btree_delete $::c1 - btree_next $::c1 - btree_next $::c1 } + # Freed 45 blocks. Total freespace is 458 + # Keys remaining are even numbers between 1000 and 1090, inclusive lrange [btree_cursor_dump $::c1] 4 5 -} {512 19} +} {458 46} #btree_page_dump $::b1 2 do_test btree-7.4 { - btree_insert $::c1 018 {*** 018 ***+++} + # The largest free block is 10 bytes long. So if we insert + # a record bigger than 10 bytes it should force a defrag + # The record is 20 bytes long. + btree_insert $::c1 2000 {123456789_12345} + btree_move_to $::c1 2000 btree_key $::c1 -} {018} +} {2000} do_test btree-7.5 { lrange [btree_cursor_dump $::c1] 4 5 -} {480 1} +} {438 1} #btree_page_dump $::b1 2 # Delete an entry to make a hole of a known size, then immediately recreate # that entry. This tests the path into allocateSpace where the hole exactly # matches the size of the desired space. # +# Keys are even numbers between 1000 and 1090 and one record of 2000. +# There are 47 keys total. +# do_test btree-7.6 { - btree_move_to $::c1 007 + btree_move_to $::c1 1006 btree_delete $::c1 - btree_move_to $::c1 011 + btree_move_to $::c1 1010 btree_delete $::c1 } {} do_test btree-7.7 { - lindex [btree_cursor_dump $::c1] 5 -} {3} + lrange [btree_cursor_dump $::c1] 4 5 +} {458 3} ;# Create two new holes of 10 bytes each #btree_page_dump $::b1 2 do_test btree-7.8 { - btree_insert $::c1 007 {*** 007 ***} - lindex [btree_cursor_dump $::c1] 5 -} {2} + btree_insert $::c1 1006 { 1006} + lrange [btree_cursor_dump $::c1] 4 5 +} {448 2} ;# Filled in the first hole #btree_page_dump $::b1 2 # Make sure the freeSpace() routine properly coaleses adjacent memory blocks # do_test btree-7.9 { - btree_move_to $::c1 013 + btree_move_to $::c1 1012 btree_delete $::c1 lrange [btree_cursor_dump $::c1] 4 5 -} {536 2} +} {458 2} ;# Coalesce with the whole before do_test btree-7.10 { - btree_move_to $::c1 009 + btree_move_to $::c1 1008 btree_delete $::c1 lrange [btree_cursor_dump $::c1] 4 5 -} {564 2} +} {468 2} ;# Coalesce with whole after do_test btree-7.11 { - btree_move_to $::c1 018 + btree_move_to $::c1 1030 btree_delete $::c1 lrange [btree_cursor_dump $::c1] 4 5 -} {596 2} +} {478 3} ;# Make a new hole do_test btree-7.13 { - btree_move_to $::c1 033 + btree_move_to $::c1 1034 btree_delete $::c1 lrange [btree_cursor_dump $::c1] 4 5 -} {624 3} +} {488 4} ;# Make another hole do_test btree-7.14 { - btree_move_to $::c1 035 + btree_move_to $::c1 1032 btree_delete $::c1 lrange [btree_cursor_dump $::c1] 4 5 -} {652 2} +} {498 3} ;# The freed space should coalesce on both ends #btree_page_dump $::b1 2 do_test btree-7.15 { lindex [btree_pager_stats $::b1] 1 -} {2} +} {1} # Check to see that data on overflow pages work correctly. # do_test btree-8.1 { set data "*** This is a very long key " - while {[string length $data]<256} {append data $data} + while {[string length $data]<1234} {append data $data} set ::data $data - btree_insert $::c1 020 $data + btree_insert $::c1 2020 $data } {} #btree_page_dump $::b1 2 do_test btree-8.1.1 { lindex [btree_pager_stats $::b1] 1 -} {2} +} {1} #btree_pager_ref_dump $::b1 do_test btree-8.2 { + btree_move_to $::c1 2020 string length [btree_data $::c1] } [string length $::data] do_test btree-8.3 { @@ -638,9 +644,10 @@ do_test btree-8.5 { set data "*** This is an even longer key" while {[string length $data]<2000} {append data $data} set ::data $data - btree_insert $::c1 020 $data + btree_insert $::c1 2030 $data } {} do_test btree-8.6 { + btree_move_to 2030 string length [btree_data $::c1] } [string length $::data] do_test btree-8.7 { @@ -654,8 +661,8 @@ do_test btree-8.9 { btree_close_cursor $::c1 btree_close $::b1 set ::b1 [btree_open test1.bt 2000 0] - set ::c1 [btree_cursor $::b1 2 1] - btree_move_to $::c1 020 + set ::c1 [btree_cursor $::b1 1 1] + btree_move_to $::c1 2030 btree_data $::c1 } $::data do_test btree-8.10 { @@ -664,7 +671,7 @@ do_test btree-8.10 { } {} do_test btree-8.11 { lindex [btree_get_meta $::b1] 0 -} [expr {int(([string length $::data]-238+1019)/1020)}] +} {} # Now check out keys on overflow pages. #