More bug fixes in btree.c. (CVS 1322)

FossilOrigin-Name: a80939ef714ec884950b4a1f4f809ffa37fdfa59
This commit is contained in:
drh 2004-05-07 23:50:57 +00:00
parent de64713041
commit c39e000bbf
6 changed files with 244 additions and 186 deletions

View File

@ -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

View File

@ -1 +1 @@
d394b2b217d4d728f9eba397262bf9d36195719e
a80939ef714ec884950b4a1f4f809ffa37fdfa59

View File

@ -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->idx<pPage->nCell );
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->idx<pPage->nCell );
@ -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->idx<pCur->pPage->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->idx<pCur->pPage->nCell );
pBt = pCur->pBt;
pPage = pCur->pPage;
assert( pCur->idx>=0 && pCur->idx<pPage->nCell );
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->idx<pPage->nCell );
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->idx<pCur->pPage->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->idx<pPage->nCell );
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->idx<pCur->pPage->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->idx<pPage->nCell );
if( pCur->eSkip==SKIP_NEXT ){
pCur->eSkip = SKIP_NONE;
*pRes = 0;
return SQLITE_OK;
}
pCur->eSkip = SKIP_NONE;
pCur->idx++;
if( pCur->idx>=pPage->nCell ){
if( !pPage->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 && idx<SQLITE_USABLE_SIZE ){
while( idx>0 && idx<pPage->pBt->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 && idx<SQLITE_USABLE_SIZE ){
while( idx>0 && idx<pPage->pBt->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);

View File

@ -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*);

View File

@ -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 },

View File

@ -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.
#