Extra tests and resulting bugfixes for btree cursors. (CVS 2106)

FossilOrigin-Name: e1530854c9004c25f5ffa21f9cfb9c44c83cc7f0
This commit is contained in:
danielk1977 2004-11-17 10:22:03 +00:00
parent 602479eae8
commit c7dc75334f
4 changed files with 179 additions and 23 deletions

View File

@ -1,5 +1,5 @@
C Clarify\sthe\sLIMIT\sclause\sin\sthe\sdocumentation.\s\sTicket\s#1002.\s(CVS\s2105)
D 2004-11-16T23:21:57
C Extra\stests\sand\sresulting\sbugfixes\sfor\sbtree\scursors.\s(CVS\s2106)
D 2004-11-17T10:22:03
F Makefile.in e747bb5ba34ccbdd81f79dcf1b2b33c02817c21d
F Makefile.linux-gcc a9e5a0d309fa7c38e7c14d3ecf7690879d3a5457
F README a01693e454a00cc117967e3f9fdab2d4d52e9bc1
@ -29,7 +29,7 @@ F sqlite3.def dbaeb20c153e1d366e8f421b55a573f5dfc00863
F sqlite3.pc.in 985b9bf34192a549d7d370e0f0b6b34a4f61369a
F src/attach.c e49d09dad9f5f9fb10b4b0c1be5a70ae4c45e689
F src/auth.c 3b81f2a42f48a62c2c9c9b0eda31a157c681edea
F src/btree.c c878e87a415a429a335bf26d834a311c5c40f5d1
F src/btree.c 49b09718cd988d1c7c981b03e94679bc10b5f711
F src/btree.h 861e40b759a195ba63819740e484390012cf81ab
F src/build.c a95eb1181247368b0ffe2eed121a43735976a964
F src/date.c 65536e7ea04fdde6e0551264fca15966966e171f
@ -101,7 +101,7 @@ F test/btree4.test 3797b4305694c7af6828675b0f4b1424b8ca30e4
F test/btree5.test 8e5ff32c02e685d36516c6499add9375fe1377f2
F test/btree6.test a5ede6bfbbb2ec8b27e62813612c0f28e8f3e027
F test/btree7.test a6d3b842db22af97dd14b989e90a2fd96066b72f
F test/btree8.test 12db22f6963d9b33a5aa8e8b766033afc82b22c2
F test/btree8.test d4e5932e54ae10f934d92ebaff94b594923d9ebc
F test/capi2.test cd5e149564094bda9a587e70ec5949863222cd23
F test/capi3.test da88858ea5318c0cbd0990be9d8db0237496a3dc
F test/capi3b.test 5b6a66f9f295f79f443b5d3f33187fa5ef6cf336
@ -258,7 +258,7 @@ F www/tclsqlite.tcl 560ecd6a916b320e59f2917317398f3d59b7cc25
F www/vdbe.tcl 095f106d93875c94b47367384ebc870517431618
F www/version3.tcl 092a01f5ef430d2c4acc0ae558d74c4bb89638a0
F www/whentouse.tcl fdacb0ba2d39831e8a6240d05a490026ad4c4e4c
P a2e1c35b327e33684ab19e5f65727c42c7b2949c
R 6e228a47591f03e112c91e1ff652d971
U drh
Z 79f791e3dea5459910a0112484707216
P e05f52d907e267b4f9ea204427229e7d7ef58641
R 41b0c2001282ed7479703649a89b1f7a
U danielk1977
Z f5df5e3af5cd05954b652b1bc18662f8

View File

@ -1 +1 @@
e05f52d907e267b4f9ea204427229e7d7ef58641
e1530854c9004c25f5ffa21f9cfb9c44c83cc7f0

View File

@ -9,7 +9,7 @@
** May you share freely, never taking more than you give.
**
*************************************************************************
** $Id: btree.c,v 1.219 2004/11/16 15:50:20 danielk1977 Exp $
** $Id: btree.c,v 1.220 2004/11/17 10:22:03 danielk1977 Exp $
**
** This file implements a external (disk-based) database using BTrees.
** For a detailed discussion of BTrees, refer to
@ -3951,6 +3951,18 @@ static int balance_nonroot(MemPage *pPage){
/* If the cursor is not valid, do not do anything with it. */
if( !pCur->isValid ) continue;
/* If the cursor pointed to one of the cells moved around during the
** balancing, then set variable iCell to the index of the cell in apCell.
** This is used by the block below to figure out where the cell was
** moved to, and adjust the cursor appropriately.
**
** If the cursor points to the parent page, but the cell was not involved
** in the balance, then declare the cache of the cell-parse invalid, as a
** defragmentation may of occured during the balance. Also, if the index
** of the cell is greater than that of the divider cells, then it may
** need to be adjusted (in case there are now more or less divider cells
** than there were before the balancing).
*/
for(i=0; iCell<0 && i<nOld; i++){
if( pgno==apCopy[i]->pgno ){
iCell = nCellCnt + pCur->idx;
@ -3958,7 +3970,6 @@ static int balance_nonroot(MemPage *pPage){
}
nCellCnt += (apCopy[i]->nCell + apCopy[i]->nOverflow) + (leafData?0:1);
}
if( pgno==pParent->pgno ){
assert( !leafData );
assert( iCell==-1 );
@ -3971,10 +3982,14 @@ static int balance_nonroot(MemPage *pPage){
TRACE(("BALANCE: Cursor %p migrates from %d,%d to %d,%d\n",
pCur, pgno, pCur->idx, pgno, pCur->idx+(nNew-nOld)));
pCur->idx += (nNew-nOld);
pCur->info.nSize = 0;
}
pCur->info.nSize = 0;
}
/* If iCell is greater than or equal to zero, then pCur points at a
** cell that was moved around during the balance. Figure out where
** the cell was moved to and adjust pCur to match.
*/
if( iCell>=0 ){
int idxNew;
Pgno pgnoNew;
@ -4247,6 +4262,7 @@ static int balance_deeper(MemPage *pPage){
cdata = pChild->aData;
memcpy(cdata, &data[hdr], pPage->cellOffset+2*pPage->nCell-hdr);
memcpy(&cdata[brk], &data[brk], usableSize-brk);
assert( pChild->isInit==0 );
rc = initPage(pChild, pPage);
if( rc ) return rc;
memcpy(pChild->aOvfl, pPage->aOvfl, pPage->nOverflow*sizeof(pPage->aOvfl[0]));
@ -4524,8 +4540,9 @@ int sqlite3BtreeDelete(BtCursor *pCur){
}
rc = sqlite3pager_write(leafCur.pPage->aData);
if( rc ) goto delete_out;
TRACE(("DELETE: table=%d delete internal from %d replace from leaf %d\n",
pCur->pgnoRoot, pPage->pgno, leafCur.pPage->pgno));
TRACE(("DELETE: table=%d delete internal from %d,%d replace "
"from leaf %d,%d\n", pCur->pgnoRoot, pPage->pgno, idx,
leafCur.pPage->pgno, leafCur.idx));
/* Drop the cell from the internal page. Make a copy of the cell from
** the leaf page into memory obtained from malloc(). Insert it into
@ -4549,6 +4566,10 @@ int sqlite3BtreeDelete(BtCursor *pCur){
/* If there are any cursors that point to the leaf-cell, move them
** so that they point at internal cell. This is easiest done by
** calling BtreePrevious().
**
** Also, any cursors that point to the internal page have their
** cached parses invalidated, as the insertCell() above may have
** caused a defragmation.
*/
for(pCur2=pBt->pCursor; pCur2; pCur2 = pCur2->pNext){
if( pCur2->pPage==leafCur.pPage && pCur2->idx==leafCur.idx ){
@ -4563,6 +4584,9 @@ int sqlite3BtreeDelete(BtCursor *pCur){
assert( pCur2->idx==idx );
pCur2->delShift = delShiftSave;
}
if( pCur2->pPage==pPage ){
pCur2->info.nSize = 0;
}
}
/* Balance the internal page. Free the memory allocated for the
@ -4575,8 +4599,8 @@ int sqlite3BtreeDelete(BtCursor *pCur){
for(pCur2=pBt->pCursor; pCur2; pCur2 = pCur2->pNext){
if( pCur2->pPage==leafCur.pPage && pCur2->idx>leafCur.idx ){
TRACE(("DELETE: Cursor %p migrates from %d,%d to %d,%d\n",
pCur2, pPage->pgno, pCur2->idx, pPage->pgno, pCur2->idx-1));
TRACE(("DELETE: Cursor %p migrates from %d,%d to %d,%d\n", pCur2,
leafCur.pPage->pgno,pCur2->idx,leafCur.pPage->pgno, pCur2->idx-1));
pCur2->idx--;
pCur2->info.nSize = 0;
}
@ -4988,7 +5012,7 @@ int sqlite3BtreeFlags(BtCursor *pCur){
** is used for debugging and testing only.
*/
#ifdef SQLITE_TEST
int sqlite3BtreePageDump(Btree *pBt, int pgno, int recursive){
static int btreePageDump(Btree *pBt, int pgno, int recursive, MemPage *pParent){
int rc;
MemPage *pPage;
int i, j, c;
@ -5004,7 +5028,7 @@ int sqlite3BtreePageDump(Btree *pBt, int pgno, int recursive){
rc = getPage(pBt, (Pgno)pgno, &pPage);
isInit = pPage->isInit;
if( pPage->isInit==0 ){
initPage(pPage, 0);
initPage(pPage, pParent);
}
if( rc ){
return rc;
@ -5074,16 +5098,19 @@ int sqlite3BtreePageDump(Btree *pBt, int pgno, int recursive){
if( recursive && !pPage->leaf ){
for(i=0; i<nCell; i++){
unsigned char *pCell = findCell(pPage, i);
sqlite3BtreePageDump(pBt, get4byte(pCell), 1);
btreePageDump(pBt, get4byte(pCell), 1, pPage);
idx = get2byte(pCell);
}
sqlite3BtreePageDump(pBt, get4byte(&data[hdr+8]), 1);
btreePageDump(pBt, get4byte(&data[hdr+8]), 1, pPage);
}
pPage->isInit = isInit;
sqlite3pager_unref(data);
fflush(stdout);
return SQLITE_OK;
}
int sqlite3BtreePageDump(Btree *pBt, int pgno, int recursive){
return btreePageDump(pBt, pgno, recursive, 0);
}
#endif
#ifdef SQLITE_TEST

View File

@ -13,7 +13,7 @@
# this file tests that existing cursors are correctly repositioned
# when entries are inserted into or deleted from btrees.
#
# $Id: btree8.test,v 1.3 2004/11/16 15:50:21 danielk1977 Exp $
# $Id: btree8.test,v 1.4 2004/11/17 10:22:04 danielk1977 Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
@ -26,7 +26,6 @@ source $testdir/tester.tcl
# btree-8.4.*: Test cursor persistence when deleting records from indices.
#
# Transform the number $num into a string of length $len by repeating the
# string representation of the number as many times as necessary. Repeats
# are seperated by a '.' character. Eg:
@ -92,8 +91,12 @@ db close
#
# Open the database at the btree level and begin a transaction
do_test btree8-1.1 {
do_test btree8-1.0 {
set ::bt [btree_open test.db 100 0]
expr 0
} {0}
do_test btree8-1.1 {
btree_begin_transaction $::bt
expr 0
} {0}
@ -280,5 +283,131 @@ foreach csr $csr_list {
}
set csr_list [list]
#------------------------------------------------------------------------
# Tests btree8.5.* also test the types of trees used for SQL indices.
# This time, 300 entries of 150 bytes each are inserted into the btree (this
# produces a tree of height 3 - root page is the grandparent of the leaves).
# A cursor points at each entry. We check that all cursors retain there
# validity when:
#
# * Each entry is deleted (test cases btree-8.5.1.*)
# * An entry is inserted just after/before each existing key (test
# cases btree-8.5.2.*).
#
# Open a cursor on each entry in the tree in B-tree $bt, root-page $tnum.
# Return a list of the cursors.
#
proc open_cursors {bt tnum} {
set c [btree_cursor $bt $tnum 0]
set csr_list [list]
for {btree_first $c} {![btree_eof $c]} {btree_next $c} {
set c2 [btree_cursor $bt $tnum 0]
btree_move_to $c2 [btree_key $c]
lappend csr_list $c2
}
btree_close_cursor $c
return $csr_list
}
# Close all cursors in the list $csr_list.
#
proc close_cursors {csr_list} {
foreach c $csr_list {
btree_close_cursor $c
}
}
# Check that the key for each cursor in csr_list matches the corresponding
# entry in key_list. If not, raise an exception.
#
proc check_cursors {key_list csr_list} {
foreach k $key_list c $csr_list {
if {[string compare $k [btree_key $c]]} {
error "Csr key '[btree_key $c]' - should be '$k'"
}
}
}
# Set up the table used for the btree-8.5.* tests
do_test btree-8.5.0 {
btree_begin_transaction $::bt
set c [btree_cursor $::bt $::inum 1]
for {set i 2} {$i<=600} {incr i 2} {
set key [num_to_string $i 150]
lappend key_list $key
btree_insert $c $key ""
}
btree_close_cursor $c
btree_commit $::bt
} {}
# Test cases btree-8.5.1.* - Check that cursors survive DELETE operations.
set testnum 0
foreach key [lrange $::key_list 0 0] {
incr testnum
btree_begin_transaction $::bt
# Open the 300 cursors.
do_test btree-8.5.1.$testnum.1 {
set ::csr_list [open_cursors $::bt $::inum]
llength $::csr_list
} {300}
# Delete an entry.
do_test btree-8.5.1.$testnum.2 {
set c [btree_cursor $::bt $::inum 1]
btree_move_to $c $::key
btree_delete $c
btree_close_cursor $c
} {}
# Check that all 300 cursors are Ok.
do_test btree-8.5.1.$testnum.3 {
catch {
set e [lsearch $::key_list $::key]
check_cursors [lreplace $::key_list $e $e ""] $::csr_list
} msg
set msg
} {}
close_cursors $::csr_list
btree_rollback $::bt
}
# Test cases btree-8.5.2.* - Check that cursors survive INSERT operations.
set testnum 0
foreach key $::key_list {
incr testnum
btree_begin_transaction $::bt
# Open the 300 cursors.
do_test btree-8.5.2.$testnum.1 {
set ::csr_list [open_cursors $::bt $::inum]
llength $::csr_list
} {300}
# Insert new entries, one before the key, and one after.
do_test btree-8.5.2.$testnum.2 {
set c [btree_cursor $::bt $::inum 1]
btree_insert $c "$::key$::key" ""
btree_insert $c [string range $::key 0 end-1] ""
btree_close_cursor $c
} {}
# Check that all 300 cursors are Ok.
do_test btree-8.5.2.$testnum.3 {
catch {
check_cursors $::key_list $::csr_list
} msg
set msg
} {}
close_cursors $::csr_list
btree_rollback $::bt
}
finish_test