From c7dc75334ff3f1c0d5a1faa1f88bb6f5e9c854f0 Mon Sep 17 00:00:00 2001 From: danielk1977 Date: Wed, 17 Nov 2004 10:22:03 +0000 Subject: [PATCH] Extra tests and resulting bugfixes for btree cursors. (CVS 2106) FossilOrigin-Name: e1530854c9004c25f5ffa21f9cfb9c44c83cc7f0 --- manifest | 16 +++--- manifest.uuid | 2 +- src/btree.c | 49 +++++++++++++---- test/btree8.test | 135 +++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 179 insertions(+), 23 deletions(-) diff --git a/manifest b/manifest index 9dc6cb038c..64fbf45085 100644 --- a/manifest +++ b/manifest @@ -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 diff --git a/manifest.uuid b/manifest.uuid index 0b829bcbc7..8a18be2105 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e05f52d907e267b4f9ea204427229e7d7ef58641 \ No newline at end of file +e1530854c9004c25f5ffa21f9cfb9c44c83cc7f0 \ No newline at end of file diff --git a/src/btree.c b/src/btree.c index 4aa6538a44..3947b57ed7 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.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 && ipgno ){ 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; iisInit = 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 diff --git a/test/btree8.test b/test/btree8.test index 2e5b0529ba..2f63618edf 100644 --- a/test/btree8.test +++ b/test/btree8.test @@ -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