-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
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
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
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
** 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
/* 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;
}
nCellCnt += (apCopy[i]->nCell + apCopy[i]->nOverflow) + (leafData?0:1);
}
-
if( pgno==pParent->pgno ){
assert( !leafData );
assert( iCell==-1 );
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;
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]));
}
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
/* 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 ){
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
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;
}
** 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;
rc = getPage(pBt, (Pgno)pgno, &pPage);
isInit = pPage->isInit;
if( pPage->isInit==0 ){
- initPage(pPage, 0);
+ initPage(pPage, pParent);
}
if( rc ){
return rc;
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
# 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
# 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:
#
# 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}
}
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