]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Extra tests and resulting bugfixes for btree cursors. (CVS 2106)
authordanielk1977 <danielk1977@noemail.net>
Wed, 17 Nov 2004 10:22:03 +0000 (10:22 +0000)
committerdanielk1977 <danielk1977@noemail.net>
Wed, 17 Nov 2004 10:22:03 +0000 (10:22 +0000)
FossilOrigin-Name: e1530854c9004c25f5ffa21f9cfb9c44c83cc7f0

manifest
manifest.uuid
src/btree.c
test/btree8.test

index 9dc6cb038ca83d625b211a03c8741f08b3500464..64fbf45085bb742541a0c7db8fa660505e6899cf 100644 (file)
--- 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
index 0b829bcbc782a2b943175329a8669f87f111e139..8a18be21059f394a30d2b7a15af111e71ddb8ba4 100644 (file)
@@ -1 +1 @@
-e05f52d907e267b4f9ea204427229e7d7ef58641
\ No newline at end of file
+e1530854c9004c25f5ffa21f9cfb9c44c83cc7f0
\ No newline at end of file
index 4aa6538a44a2535ca888795db4ab89f582ff28de..3947b57ed78dfac070869bb0e52f3c542156b9dc 100644 (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
index 2e5b0529ba963504345f372998c89247bbc6bd14..2f63618edf3a6dd6b09ed09d4f86c9ae2d2fc120 100644 (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