1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-30 19:03:16 +03:00

More tests and bug fixes in btree.c (CVS 229)

FossilOrigin-Name: 6b9b298b2846146b95d7df7f423867976bafa390
This commit is contained in:
drh
2001-06-25 02:11:07 +00:00
parent 7c717f750e
commit 2aa679f604
6 changed files with 382 additions and 55 deletions

View File

@ -1,5 +1,5 @@
C The\sfirst\stest\sfile\sfor\sBTree\sadded.\sSimple\sinsert\sand\sdelete\stests\spass.\nThere\sis\sstill\sa\slot\sof\swork\sto\sbe\sdone,\sthough.\s(CVS\s228) C More\stests\sand\sbug\sfixes\sin\sbtree.c\s(CVS\s229)
D 2001-06-24T20:39:41 D 2001-06-25T02:11:07
F COPYRIGHT 74a8a6531a42e124df07ab5599aad63870fa0bd4 F COPYRIGHT 74a8a6531a42e124df07ab5599aad63870fa0bd4
F Makefile.in 65862a30703b070209b5f5e565d75cc870962b3c F Makefile.in 65862a30703b070209b5f5e565d75cc870962b3c
F README 51f6a4e7408b34afa5bc1c0485f61b6a4efb6958 F README 51f6a4e7408b34afa5bc1c0485f61b6a4efb6958
@ -12,8 +12,8 @@ F notes/notes1.txt b7c0812b704a022e88c621146ae50955c923d464
F notes/notes2.txt 7e3fafd5e25906c1fe1e95f13b089aa398ca403e F notes/notes2.txt 7e3fafd5e25906c1fe1e95f13b089aa398ca403e
F notes/notes3.txt 985bf688b59f1f52bfe6e4b1f896efdeffac1432 F notes/notes3.txt 985bf688b59f1f52bfe6e4b1f896efdeffac1432
F src/TODO 38a68a489e56e9fd4a96263e0ff9404a47368ad4 F src/TODO 38a68a489e56e9fd4a96263e0ff9404a47368ad4
F src/btree.c e46ab610d0bef3d1a9f698bede21d554c42763e5 F src/btree.c 977621ad8c0607c13e4f404284ae1fb28adf542f
F src/btree.h 40ae2c9b6d2ba8feb03461a589ccab9afc04ec29 F src/btree.h 2ce445f0b733e0d077377cbb49c12316e7ce524d
F src/build.c 4f6a2d551c56342cd4a0420654835be3ad179651 F src/build.c 4f6a2d551c56342cd4a0420654835be3ad179651
F src/dbbe.c b18259f99d87240cbe751021cf14dd3aa83a48af F src/dbbe.c b18259f99d87240cbe751021cf14dd3aa83a48af
F src/dbbe.h 7235b15c6c5d8be0c4da469cef9620cee70b1cc8 F src/dbbe.h 7235b15c6c5d8be0c4da469cef9620cee70b1cc8
@ -45,7 +45,7 @@ F src/table.c adcaf074f6c1075e86359174e68701fa2acfc4d6
F src/tclsqlite.c af29a45cb4c2244a6fd032568a22d26516472b2c F src/tclsqlite.c af29a45cb4c2244a6fd032568a22d26516472b2c
F src/test1.c abb3cb427e735ae87e6533f5b3b7164b7da91bc4 F src/test1.c abb3cb427e735ae87e6533f5b3b7164b7da91bc4
F src/test2.c 0183625225a860397b4fd3041aefb48f77e4630a F src/test2.c 0183625225a860397b4fd3041aefb48f77e4630a
F src/test3.c 405ea28287faeefc108ca362eca527731421e6bb F src/test3.c a66bb93c540d53d1026b0d183faca928d6c82ba0
F src/tokenize.c 0118b57702cb6550769316e8443b06760b067acf F src/tokenize.c 0118b57702cb6550769316e8443b06760b067acf
F src/update.c 0cf789656a936d4356668393267692fa4b03ffc6 F src/update.c 0cf789656a936d4356668393267692fa4b03ffc6
F src/util.c 1b396ac34e30dd6222d82e996c17b161bbc906bc F src/util.c 1b396ac34e30dd6222d82e996c17b161bbc906bc
@ -53,7 +53,7 @@ F src/vdbe.c f93be4414ba892df9c5589815d2a57c1fb12c820
F src/vdbe.h dc1205da434c6a9da03b5d6b089270bbc8e6d437 F src/vdbe.h dc1205da434c6a9da03b5d6b089270bbc8e6d437
F src/where.c 0c542fc44bd85152dfb8507862cfe2e60c629e9f F src/where.c 0c542fc44bd85152dfb8507862cfe2e60c629e9f
F test/all.test 21d55a97e39e7ec5776751dc9dd8b1b51ef4a048 F test/all.test 21d55a97e39e7ec5776751dc9dd8b1b51ef4a048
F test/btree.test 8db61fdd957e906d7d237bf0f578f3e8cc9fb33e F test/btree.test 9207999792e0a784821fdfa1287311e3f22ff4b0
F test/copy.test b77a1214bd7756f2849d5c4fa6e715c0ff0c34eb F test/copy.test b77a1214bd7756f2849d5c4fa6e715c0ff0c34eb
F test/dbbe.test a022fe2d983848f786e17ef1fc6809cfd37fb02c F test/dbbe.test a022fe2d983848f786e17ef1fc6809cfd37fb02c
F test/delete.test 50b9b1f06c843d591741dba7869433a105360dbf F test/delete.test 50b9b1f06c843d591741dba7869433a105360dbf
@ -108,7 +108,7 @@ F www/opcode.tcl cb3a1abf8b7b9be9f3a228d097d6bf8b742c2b6f
F www/sqlite.tcl cb0d23d8f061a80543928755ec7775da6e4f362f F www/sqlite.tcl cb0d23d8f061a80543928755ec7775da6e4f362f
F www/tclsqlite.tcl 06f81c401f79a04f2c5ebfb97e7c176225c0aef2 F www/tclsqlite.tcl 06f81c401f79a04f2c5ebfb97e7c176225c0aef2
F www/vdbe.tcl 0c8aaa529dd216ccbf7daaabd80985e413d5f9ad F www/vdbe.tcl 0c8aaa529dd216ccbf7daaabd80985e413d5f9ad
P f4df6664037c68e1ce539c84c852124d95cd5a56 P 85f015c9750a5eab274e82f0e2c6e8f09dc7ca70
R 3068b07f88f8e6db3749d5669bb8ecc6 R 3996a4b0538db0f013a966fe30254459
U drh U drh
Z ee5edd13b8c55ec829c3087fe59854aa Z 373fed59edcfc36190ad26edeac1578b

View File

@ -1 +1 @@
85f015c9750a5eab274e82f0e2c6e8f09dc7ca70 6b9b298b2846146b95d7df7f423867976bafa390

View File

@ -21,7 +21,7 @@
** http://www.hwaci.com/drh/ ** http://www.hwaci.com/drh/
** **
************************************************************************* *************************************************************************
** $Id: btree.c,v 1.14 2001/06/24 20:39:41 drh Exp $ ** $Id: btree.c,v 1.15 2001/06/25 02:11:07 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
@ -141,7 +141,8 @@ struct PageOne {
char zMagic[MAGIC_SIZE]; /* String that identifies the file as a database */ char zMagic[MAGIC_SIZE]; /* String that identifies the file as a database */
int iMagic; /* Integer to verify correct byte order */ int iMagic; /* Integer to verify correct byte order */
Pgno freeList; /* First free page in a list of all free pages */ Pgno freeList; /* First free page in a list of all free pages */
int aMeta[SQLITE_N_BTREE_META]; /* User defined integers */ int nFree; /* Number of pages on the free list */
int aMeta[SQLITE_N_BTREE_META-1]; /* User defined integers */
}; };
/* /*
@ -205,7 +206,7 @@ struct CellHdr {
** This number is chosen so that at least 4 cells will fit on every page. ** This number is chosen so that at least 4 cells will fit on every page.
*/ */
#define MX_LOCAL_PAYLOAD \ #define MX_LOCAL_PAYLOAD \
((SQLITE_PAGE_SIZE-sizeof(PageHdr))/4-(sizeof(CellHdr)+sizeof(Pgno))) (((SQLITE_PAGE_SIZE-sizeof(PageHdr))/4-(sizeof(CellHdr)+sizeof(Pgno)))&~3)
/* /*
** Data on a database page is stored as a linked list of Cell structures. ** Data on a database page is stored as a linked list of Cell structures.
@ -364,7 +365,7 @@ static void defragmentPage(MemPage *pPage){
pPage->u.hdr.firstCell = pc; pPage->u.hdr.firstCell = pc;
memcpy(newPage, pPage->u.aDisk, pc); memcpy(newPage, pPage->u.aDisk, pc);
for(i=0; i<pPage->nCell; i++){ for(i=0; i<pPage->nCell; i++){
Cell *pCell = (Cell*)&pPage->apCell[i]; Cell *pCell = pPage->apCell[i];
/* This routine should never be called on an overfull page. The /* This routine should never be called on an overfull page. The
** following asserts verify that constraint. */ ** following asserts verify that constraint. */
@ -372,13 +373,16 @@ static void defragmentPage(MemPage *pPage){
assert( Addr(pCell) < Addr(pPage) + SQLITE_PAGE_SIZE ); assert( Addr(pCell) < Addr(pPage) + SQLITE_PAGE_SIZE );
n = cellSize(pCell); n = cellSize(pCell);
pCell->h.iNext = i<pPage->nCell-1 ? pc + n : 0; pCell->h.iNext = pc + n;
memcpy(&newPage[pc], pCell, n); memcpy(&newPage[pc], pCell, n);
pPage->apCell[i] = (Cell*)&pPage->u.aDisk[pc]; pPage->apCell[i] = (Cell*)&pPage->u.aDisk[pc];
pc += n; pc += n;
} }
assert( pPage->nFree==SQLITE_PAGE_SIZE-pc ); assert( pPage->nFree==SQLITE_PAGE_SIZE-pc );
memcpy(pPage->u.aDisk, newPage, pc); memcpy(pPage->u.aDisk, newPage, pc);
if( pPage->nCell>0 ){
pPage->apCell[pPage->nCell-1]->h.iNext = 0;
}
pFBlk = (FreeBlk*)&pPage->u.aDisk[pc]; pFBlk = (FreeBlk*)&pPage->u.aDisk[pc];
pFBlk->iSize = SQLITE_PAGE_SIZE - pc; pFBlk->iSize = SQLITE_PAGE_SIZE - pc;
pFBlk->iNext = 0; pFBlk->iNext = 0;
@ -729,12 +733,11 @@ static void unlockBtree(Btree *pBt){
} }
/* /*
** Commit the transaction currently in progress. All cursors ** Commit the transaction currently in progress.
** must be closed before this routine is called.
*/ */
int sqliteBtreeCommit(Btree *pBt){ int sqliteBtreeCommit(Btree *pBt){
int rc; int rc;
if( pBt->pCursor!=0 || pBt->inTrans==0 ) return SQLITE_ERROR; if( pBt->inTrans==0 ) return SQLITE_ERROR;
rc = sqlitepager_commit(pBt->pPager); rc = sqlitepager_commit(pBt->pPager);
pBt->inTrans = 0; pBt->inTrans = 0;
unlockBtree(pBt); unlockBtree(pBt);
@ -890,7 +893,7 @@ static int getPayload(BtCursor *pCur, int offset, int amt, char *zBuf){
if( a==amt ){ if( a==amt ){
return SQLITE_OK; return SQLITE_OK;
} }
offset += a; offset = 0;
zBuf += a; zBuf += a;
amt -= a; amt -= a;
} }
@ -910,10 +913,12 @@ static int getPayload(BtCursor *pCur, int offset, int amt, char *zBuf){
a = OVERFLOW_SIZE - offset; a = OVERFLOW_SIZE - offset;
} }
memcpy(zBuf, &pOvfl->aPayload[offset], a); memcpy(zBuf, &pOvfl->aPayload[offset], a);
offset = 0;
amt -= a; amt -= a;
zBuf += a; zBuf += a;
}else{
offset -= OVERFLOW_SIZE;
} }
offset -= OVERFLOW_SIZE;
sqlitepager_unref(pOvfl); sqlitepager_unref(pOvfl);
} }
return amt==0 ? SQLITE_OK : SQLITE_CORRUPT; return amt==0 ? SQLITE_OK : SQLITE_CORRUPT;
@ -1267,9 +1272,10 @@ static int allocatePage(Btree *pBt, MemPage **ppPage, Pgno *pPgno){
return rc; return rc;
} }
pPage1->freeList = pOvfl->iNext; pPage1->freeList = pOvfl->iNext;
pPage1->nFree--;
*ppPage = (MemPage*)pOvfl; *ppPage = (MemPage*)pOvfl;
}else{ }else{
*pPgno = sqlitepager_pagecount(pBt->pPager); *pPgno = sqlitepager_pagecount(pBt->pPager) + 1;
rc = sqlitepager_get(pBt->pPager, *pPgno, (void**)ppPage); rc = sqlitepager_get(pBt->pPager, *pPgno, (void**)ppPage);
if( rc ) return rc; if( rc ) return rc;
rc = sqlitepager_write(*ppPage); rc = sqlitepager_write(*ppPage);
@ -1294,6 +1300,7 @@ static int freePage(Btree *pBt, void *pPage, Pgno pgno){
assert( pOvfl!=0 ); assert( pOvfl!=0 );
pgno = sqlitepager_pagenumber(pOvfl); pgno = sqlitepager_pagenumber(pOvfl);
} }
assert( pgno>2 );
rc = sqlitepager_write(pPage1); rc = sqlitepager_write(pPage1);
if( rc ){ if( rc ){
return rc; return rc;
@ -1311,6 +1318,7 @@ static int freePage(Btree *pBt, void *pPage, Pgno pgno){
} }
pOvfl->iNext = pPage1->freeList; pOvfl->iNext = pPage1->freeList;
pPage1->freeList = pgno; pPage1->freeList = pgno;
pPage1->nFree++;
memset(pOvfl->aPayload, 0, OVERFLOW_SIZE); memset(pOvfl->aPayload, 0, OVERFLOW_SIZE);
((MemPage*)pPage)->isInit = 0; ((MemPage*)pPage)->isInit = 0;
assert( ((MemPage*)pPage)->pParent==0 ); assert( ((MemPage*)pPage)->pParent==0 );
@ -1340,7 +1348,6 @@ static int clearCell(Btree *pBt, Cell *pCell){
rc = freePage(pBt, pOvfl, ovfl); rc = freePage(pBt, pOvfl, ovfl);
if( rc ) return rc; if( rc ) return rc;
ovfl = nextOvfl; ovfl = nextOvfl;
sqlitepager_unref(pOvfl);
} }
return SQLITE_OK; return SQLITE_OK;
} }
@ -1477,11 +1484,11 @@ static void insertCell(MemPage *pPage, int i, Cell *pCell, int sz){
int idx, j; int idx, j;
assert( i>=0 && i<=pPage->nCell ); assert( i>=0 && i<=pPage->nCell );
assert( sz==cellSize(pCell) ); assert( sz==cellSize(pCell) );
idx = allocateSpace(pPage, sz);
for(j=pPage->nCell; j>i; j--){ for(j=pPage->nCell; j>i; j--){
pPage->apCell[j] = pPage->apCell[j-1]; pPage->apCell[j] = pPage->apCell[j-1];
} }
pPage->nCell++; pPage->nCell++;
idx = allocateSpace(pPage, sz);
if( idx<=0 ){ if( idx<=0 ){
pPage->isOverfull = 1; pPage->isOverfull = 1;
pPage->apCell[i] = pCell; pPage->apCell[i] = pCell;
@ -2002,7 +2009,7 @@ int sqliteBtreeCreateTable(Btree *pBt, int *piTable){
** Erase the given database page and all its children. Return ** Erase the given database page and all its children. Return
** the page to the freelist. ** the page to the freelist.
*/ */
static int clearDatabasePage(Btree *pBt, Pgno pgno){ static int clearDatabasePage(Btree *pBt, Pgno pgno, int freePageFlag){
MemPage *pPage; MemPage *pPage;
int rc; int rc;
Cell *pCell; Cell *pCell;
@ -2015,15 +2022,22 @@ static int clearDatabasePage(Btree *pBt, Pgno pgno){
pCell = (Cell*)&pPage->u.aDisk[idx]; pCell = (Cell*)&pPage->u.aDisk[idx];
idx = pCell->h.iNext; idx = pCell->h.iNext;
if( pCell->h.leftChild ){ if( pCell->h.leftChild ){
rc = clearDatabasePage(pBt, pCell->h.leftChild); rc = clearDatabasePage(pBt, pCell->h.leftChild, 1);
if( rc ) return rc; if( rc ) return rc;
} }
rc = clearCell(pBt, pCell); rc = clearCell(pBt, pCell);
if( rc ) return rc; if( rc ) return rc;
} }
rc = clearDatabasePage(pBt, pPage->u.hdr.rightChild); if( pPage->u.hdr.rightChild ){
if( rc ) return rc; rc = clearDatabasePage(pBt, pPage->u.hdr.rightChild, 1);
return freePage(pBt, pPage, pgno); if( rc ) return rc;
}
if( freePageFlag ){
rc = freePage(pBt, pPage, pgno);
}else{
zeroPage(pPage);
}
return rc;
} }
/* /*
@ -2034,7 +2048,7 @@ int sqliteBtreeClearTable(Btree *pBt, int iTable){
if( !pBt->inTrans ){ if( !pBt->inTrans ){
return SQLITE_ERROR; /* Must start a transaction first */ return SQLITE_ERROR; /* Must start a transaction first */
} }
rc = clearDatabasePage(pBt, (Pgno)iTable); rc = clearDatabasePage(pBt, (Pgno)iTable, 0);
if( rc ){ if( rc ){
sqliteBtreeRollback(pBt); sqliteBtreeRollback(pBt);
} }
@ -2053,13 +2067,15 @@ int sqliteBtreeDropTable(Btree *pBt, int iTable){
return SQLITE_ERROR; /* Must start a transaction first */ return SQLITE_ERROR; /* Must start a transaction first */
} }
rc = sqlitepager_get(pBt->pPager, (Pgno)iTable, (void**)&pPage); rc = sqlitepager_get(pBt->pPager, (Pgno)iTable, (void**)&pPage);
if( rc==SQLITE_OK ){ if( rc ) return rc;
rc = sqliteBtreeClearTable(pBt, iTable); rc = sqliteBtreeClearTable(pBt, iTable);
if( rc ) return rc;
if( iTable>2 ){
rc = freePage(pBt, pPage, iTable);
}else{
zeroPage(pPage);
sqlitepager_unref(pPage);
} }
if( rc==SQLITE_OK && iTable!=2 ){
rc = freePage(pBt, pPage, (Pgno)iTable);
}
sqlitepager_unref(pPage);
return rc; return rc;
} }
@ -2072,7 +2088,8 @@ int sqliteBtreeGetMeta(Btree *pBt, int *aMeta){
rc = sqlitepager_get(pBt->pPager, 1, (void**)&pP1); rc = sqlitepager_get(pBt->pPager, 1, (void**)&pP1);
if( rc ) return rc; if( rc ) return rc;
memcpy(aMeta, pP1->aMeta, sizeof(pP1->aMeta)); aMeta[0] = pP1->nFree;
memcpy(&aMeta[1], pP1->aMeta, sizeof(pP1->aMeta));
sqlitepager_unref(pP1); sqlitepager_unref(pP1);
return SQLITE_OK; return SQLITE_OK;
} }
@ -2088,8 +2105,8 @@ int sqliteBtreeUpdateMeta(Btree *pBt, int *aMeta){
} }
pP1 = pBt->page1; pP1 = pBt->page1;
rc = sqlitepager_write(pP1); rc = sqlitepager_write(pP1);
if( rc ) return rc; if( rc ) return rc;
memcpy(pP1->aMeta, aMeta, sizeof(pP1->aMeta)); memcpy(pP1->aMeta, &aMeta[1], sizeof(pP1->aMeta));
return SQLITE_OK; return SQLITE_OK;
} }
@ -2116,6 +2133,7 @@ int sqliteBtreePageDump(Btree *pBt, int pgno){
Cell *pCell = (Cell*)&pPage->u.aDisk[idx]; Cell *pCell = (Cell*)&pPage->u.aDisk[idx];
int sz = cellSize(pCell); int sz = cellSize(pCell);
sprintf(range,"%d..%d", idx, idx+sz-1); sprintf(range,"%d..%d", idx, idx+sz-1);
sz = pCell->h.nKey + pCell->h.nData;
if( sz>sizeof(payload)-1 ) sz = sizeof(payload)-1; if( sz>sizeof(payload)-1 ) sz = sizeof(payload)-1;
memcpy(payload, pCell->aPayload, sz); memcpy(payload, pCell->aPayload, sz);
for(j=0; j<sz; j++){ for(j=0; j<sz; j++){
@ -2125,8 +2143,11 @@ int sqliteBtreePageDump(Btree *pBt, int pgno){
printf( printf(
"cell %2d: i=%-10s chld=%-4d nk=%-3d nd=%-3d payload=%s\n", "cell %2d: i=%-10s chld=%-4d nk=%-3d nd=%-3d payload=%s\n",
i, range, (int)pCell->h.leftChild, pCell->h.nKey, pCell->h.nData, i, range, (int)pCell->h.leftChild, pCell->h.nKey, pCell->h.nData,
pCell->aPayload payload
); );
if( pPage->apCell[i]!=pCell ){
printf("**** apCell[%d] does not match on prior entry ****\n", i);
}
i++; i++;
idx = pCell->h.iNext; idx = pCell->h.iNext;
} }
@ -2144,6 +2165,7 @@ int sqliteBtreePageDump(Btree *pBt, int pgno){
printf("freeblock %2d: i=%-10s size=%-4d total=%d\n", printf("freeblock %2d: i=%-10s size=%-4d total=%d\n",
i, range, p->iSize, nFree); i, range, p->iSize, nFree);
idx = p->iNext; idx = p->iNext;
i++;
} }
if( idx!=0 ){ if( idx!=0 ){
printf("ERROR: next freeblock index out of range: %d\n", idx); printf("ERROR: next freeblock index out of range: %d\n", idx);
@ -2155,12 +2177,40 @@ int sqliteBtreePageDump(Btree *pBt, int pgno){
#ifdef SQLITE_TEST #ifdef SQLITE_TEST
/* /*
** Put the page number and index of a cursor into aResult[0] and aResult[1] ** Fill aResult[] with information about the entry and page that the
** This routine is used for debugging and testing only. ** cursor is pointing to.
**
** aResult[0] = The page number
** aResult[1] = The entry number
** aResult[2] = Total number of entries on this page
** aResult[3] = Size of this entry
** aResult[4] = Number of free bytes on this page
** aResult[5] = Number of free blocks on the page
** aResult[6] = Page number of the left child of this entry
** aResult[7] = Page number of the right child for the whole page
*/ */
int sqliteBtreeCursorDump(BtCursor *pCur, int *aResult){ int sqliteBtreeCursorDump(BtCursor *pCur, int *aResult){
aResult[0] = sqlitepager_pagenumber(pCur->pPage); int cnt, idx;
MemPage *pPage = pCur->pPage;
aResult[0] = sqlitepager_pagenumber(pPage);
aResult[1] = pCur->idx; aResult[1] = pCur->idx;
aResult[2] = pPage->nCell;
if( pCur->idx>=0 && pCur->idx<pPage->nCell ){
aResult[3] = cellSize(pPage->apCell[pCur->idx]);
aResult[6] = pPage->apCell[pCur->idx]->h.leftChild;
}else{
aResult[3] = 0;
aResult[6] = 0;
}
aResult[4] = pPage->nFree;
cnt = 0;
idx = pPage->u.hdr.firstFree;
while( idx>0 && idx<SQLITE_PAGE_SIZE ){
cnt++;
idx = ((FreeBlk*)&pPage->u.aDisk[idx])->iNext;
}
aResult[5] = cnt;
aResult[7] = pPage->u.hdr.rightChild;
return SQLITE_OK; return SQLITE_OK;
} }
#endif #endif

View File

@ -24,7 +24,7 @@
** This header file defines the interface that the sqlite B-Tree file ** This header file defines the interface that the sqlite B-Tree file
** subsystem. ** subsystem.
** **
** @(#) $Id: btree.h,v 1.5 2001/06/22 19:15:00 drh Exp $ ** @(#) $Id: btree.h,v 1.6 2001/06/25 02:11:07 drh Exp $
*/ */
typedef struct Btree Btree; typedef struct Btree Btree;
@ -52,7 +52,7 @@ int sqliteBtreeDataSize(BtCursor*, int *pSize);
int sqliteBtreeData(BtCursor*, int offset, int amt, char *zBuf); int sqliteBtreeData(BtCursor*, int offset, int amt, char *zBuf);
int sqliteBtreeCloseCursor(BtCursor*); int sqliteBtreeCloseCursor(BtCursor*);
#define SQLITE_N_BTREE_META 3 #define SQLITE_N_BTREE_META 4
int sqliteBtreeGetMeta(Btree*, int*); int sqliteBtreeGetMeta(Btree*, int*);
int sqliteBtreeUpdateMeta(Btree*, int*); int sqliteBtreeUpdateMeta(Btree*, int*);

View File

@ -25,7 +25,7 @@
** is not included in the SQLite library. It is used for automated ** is not included in the SQLite library. It is used for automated
** testing of the SQLite library. ** testing of the SQLite library.
** **
** $Id: test3.c,v 1.2 2001/06/22 19:15:01 drh Exp $ ** $Id: test3.c,v 1.3 2001/06/25 02:11:07 drh Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
#include "pager.h" #include "pager.h"
@ -610,8 +610,17 @@ static int btree_data(
/* /*
** Usage: btree_cursor_dump ID ** Usage: btree_cursor_dump ID
** **
** Return two integers which are the page number and cell index for ** Return eight integers containing information about the entry the
** the given cursor. ** cursor is pointing to:
**
** aResult[0] = The page number
** aResult[1] = The entry number
** aResult[2] = Total number of entries on this page
** aResult[3] = Size of this entry
** aResult[4] = Number of free bytes on this page
** aResult[5] = Number of free blocks on the page
** aResult[6] = Page number of the left child of this entry
** aResult[7] = Page number of the right child for the whole page
*/ */
static int btree_cursor_dump( static int btree_cursor_dump(
void *NotUsed, void *NotUsed,
@ -621,8 +630,9 @@ static int btree_cursor_dump(
){ ){
BtCursor *pCur; BtCursor *pCur;
int rc; int rc;
int aResult[2]; int i, j;
char zBuf[50]; int aResult[8];
char zBuf[400];
if( argc!=2 ){ if( argc!=2 ){
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
@ -635,8 +645,12 @@ static int btree_cursor_dump(
Tcl_AppendResult(interp, errorName(rc), 0); Tcl_AppendResult(interp, errorName(rc), 0);
return TCL_ERROR; return TCL_ERROR;
} }
sprintf(zBuf,"%d %d",aResult[0], aResult[1]); j = 0;
Tcl_AppendResult(interp, zBuf, 0); for(i=0; i<sizeof(aResult)/sizeof(aResult[0]); i++){
sprintf(&zBuf[j]," %d", aResult[i]);
j += strlen(&zBuf[j]);
}
Tcl_AppendResult(interp, &zBuf[1], 0);
return SQLITE_OK; return SQLITE_OK;
} }

View File

@ -23,7 +23,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.1 2001/06/24 20:39:41 drh Exp $ # $Id: btree.test,v 1.2 2001/06/25 02:11:07 drh Exp $
set testdir [file dirname $argv0] set testdir [file dirname $argv0]
@ -287,9 +287,7 @@ do_test btree-4.4 {
# Commit and make sure the delete is still there. # Commit and make sure the delete is still there.
# #
do_test btree-4.5 { do_test btree-4.5 {
btree_close_cursor $::c1
btree_commit $::b1 btree_commit $::b1
set ::c1 [btree_cursor $::b1 2]
btree_move_to $::c1 {} btree_move_to $::c1 {}
set r {} set r {}
while 1 { while 1 {
@ -302,14 +300,279 @@ do_test btree-4.5 {
set r set r
} {five 5.00 four 4.00 six 6.00 three 3.00 two 2.00} } {five 5.00 four 4.00 six 6.00 three 3.00 two 2.00}
# Completely close the database and reopen it. Then check
# the data again.
#
do_test btree-4.6 {
btree_close_cursor $::c1
btree_close $::b1
set ::b1 [btree_open test1.bt]
set ::c1 [btree_cursor $::b1 2]
set r {}
while 1 {
set key [btree_key $::c1]
if {$key==""} break
lappend r $key
lappend r [btree_data $::c1]
btree_next $::c1
}
set r
} {five 5.00 four 4.00 six 6.00 three 3.00 two 2.00}
# Try to read and write meta data
#
do_test btree-5.1 {
btree_get_meta $::b1
} {0 0 0 0}
do_test btree-5.2 {
set rc [catch {btree_update_meta $::b1 1 2 3 4} msg]
lappend rc $msg
} {1 SQLITE_ERROR}
do_test btree-5.3 {
btree_begin_transaction $::b1
set rc [catch {btree_update_meta $::b1 1 2 3 4} msg]
lappend rc $msg
} {0 {}}
do_test btree-5.4 {
btree_get_meta $::b1
} {0 2 3 4}
do_test btree-5.5 {
btree_close_cursor $::c1
btree_rollback $::b1
btree_get_meta $::b1
} {0 0 0 0}
do_test btree-5.6 {
btree_begin_transaction $::b1
btree_update_meta $::b1 999 10 20 30
btree_commit $::b1
btree_get_meta $::b1
} {0 10 20 30}
proc select_all {cursor} {
set r {}
btree_move_to $cursor {}
while 1 {
set key [btree_key $cursor]
if {$key==""} break
lappend r $key
lappend r [btree_data $cursor]
btree_next $cursor
}
return $r
}
# Try to create a new table in the database file
#
do_test btree-6.1 {
set rc [catch {btree_create_table $::b1} msg]
lappend rc $msg
} {1 SQLITE_ERROR}
do_test btree-6.2 {
btree_begin_transaction $::b1
set ::t2 [btree_create_table $::b1]
} {3}
do_test btree-6.2.1 {
set ::c2 [btree_cursor $::b1 $::t2]
btree_insert $::c2 ten 10
btree_key $::c2
} {ten}
do_test btree-6.3 {
btree_commit $::b1
set ::c1 [btree_cursor $::b1 2]
select_all $::c1
} {five 5.00 four 4.00 six 6.00 three 3.00 two 2.00}
#btree_page_dump $::b1 3
do_test btree-6.4 {
select_all $::c2
} {ten 10}
# Drop the new table, then create it again anew.
#
do_test btree-6.5 {
btree_begin_transaction $::b1
} {}
do_test btree-6.6 {
btree_close_cursor $::c2
} {}
do_test btree-6.7 {
btree_drop_table $::b1 $::t2
} {}
do_test btree-6.7.1 {
lindex [btree_get_meta $::b1] 0
} {1}
do_test btree-6.8 {
set ::t2 [btree_create_table $::b1]
} {3}
do_test btree-6.8.1 {
lindex [btree_get_meta $::b1] 0
} {0}
do_test btree-6.9 {
set ::c2 [btree_cursor $::b1 $::t2]
btree_move_to $::c2 {}
btree_key $::c2
} {}
# If we drop table 2 it just clears the table. Table 2 always exists.
#
do_test btree-6.10 {
btree_close_cursor $::c1
btree_drop_table $::b1 2
set ::c1 [btree_cursor $::b1 2]
btree_move_to $::c1 {}
btree_key $::c1
} {}
do_test btree-6.11 {
btree_commit $::b1
select_all $::c1
} {}
do_test btree-6.12 {
select_all $::c2
} {}
# 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.
# 3. Insert a single entry that requires more contiguous
# space than is available.
#
do_test btree-7.1 {
btree_begin_transaction $::b1
} {}
do_test btree-7.2 {
for {set i 0} {$i<36} {incr i} {
set key [format %03d $i]
set data "*** $key ***"
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]!=""} {
btree_delete $::c1
btree_next $::c1
btree_next $::c1
}
lrange [btree_cursor_dump $::c1] 4 5
} {512 19}
#btree_page_dump $::b1 2
do_test btree-7.4 {
btree_insert $::c1 018 {*** 018 ***+++}
btree_key $::c1
} {018}
do_test btree-7.5 {
lrange [btree_cursor_dump $::c1] 4 5
} {480 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.
#
do_test btree-7.6 {
btree_move_to $::c1 007
btree_delete $::c1
btree_move_to $::c1 011
btree_delete $::c1
} {}
do_test btree-7.7 {
lindex [btree_cursor_dump $::c1] 5
} {3}
#btree_page_dump $::b1 2
do_test btree-7.8 {
btree_insert $::c1 007 {*** 007 ***}
lindex [btree_cursor_dump $::c1] 5
} {2}
#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_delete $::c1
lrange [btree_cursor_dump $::c1] 4 5
} {536 2}
do_test btree-7.10 {
btree_move_to $::c1 009
btree_delete $::c1
lrange [btree_cursor_dump $::c1] 4 5
} {564 2}
do_test btree-7.11 {
btree_move_to $::c1 018
btree_delete $::c1
lrange [btree_cursor_dump $::c1] 4 5
} {596 2}
do_test btree-7.13 {
btree_move_to $::c1 033
btree_delete $::c1
lrange [btree_cursor_dump $::c1] 4 5
} {624 3}
do_test btree-7.14 {
btree_move_to $::c1 035
btree_delete $::c1
lrange [btree_cursor_dump $::c1] 4 5
} {652 2}
#btree_page_dump $::b1 2
# Check to see that both key and 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}
set ::data $data
btree_insert $::c1 020 $data
} {}
#btree_page_dump $::b1 2
do_test btree-8.2 {
string length [btree_data $::c1]
} [string length $::data]
do_test btree-8.3 {
btree_data $::c1
} $::data
do_test btree-8.4 {
btree_delete $::c1
} {}
do_test btree-8.4.1 {
lindex [btree_get_meta $::b1] 0
} [expr {int(([string length $::data]-238+1019)/1020)}]
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
} {}
do_test btree-8.6 {
string length [btree_data $::c1]
} [string length $::data]
do_test btree-8.7 {
btree_data $::c1
} $::data
do_test btree-8.8 {
btree_commit $::b1
btree_data $::c1
} $::data
do_test btree-8.9 {
btree_close_cursor $::c1
btree_close $::b1
set ::b1 [btree_open test1.bt]
set ::c1 [btree_cursor $::b1 2]
btree_move_to $::c1 020
btree_data $::c1
} $::data
do_test btree-8.10 {
btree_begin_transaction $::b1
btree_delete $::c1
} {}
do_test btree-8.11 {
lindex [btree_get_meta $::b1] 0
} [expr {int(([string length $::data]-238+1019)/1020)}]
puts [btree_get_meta $::b1]
do_test btree-99.1 { do_test btree-99.1 {
btree_close $::b1 btree_close $::b1
} {} } {}
} ;# end if( not mem: and has pager_open command ); } ;# end if( not mem: and has pager_open command );
finish_test finish_test