]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Code toward having btree cursors persist when their table is written too. Doesn't...
authordanielk1977 <danielk1977@noemail.net>
Sat, 13 Nov 2004 13:19:56 +0000 (13:19 +0000)
committerdanielk1977 <danielk1977@noemail.net>
Sat, 13 Nov 2004 13:19:56 +0000 (13:19 +0000)
FossilOrigin-Name: 5eaa18d43f2996a9d354bb4fb9c81e267e98be29

manifest
manifest.uuid
src/btree.c
test/btree8.test [new file with mode: 0644]
test/quick.test

index d771c9679f2bda7218717954487c3c44848f7a77..345360543b737174b5dd7d586e51a254229702ac 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Fix\sthe\skeyword\sgenerator\sso\sthat\sit\sworks\swith\sSQLITE_OMIT_ALTERTABLE.\s(CVS\s2096)
-D 2004-11-13T03:59:25
+C Code\stoward\shaving\sbtree\scursors\spersist\swhen\stheir\stable\sis\swritten\stoo.\sDoesn't\swork\sproperly\syet.\s(CVS\s2097)
+D 2004-11-13T13:19:56
 F Makefile.in c4d2416860f472a1e3393714d0372074197565df
 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 9fd74df65bad768a441afefc3b73174d45b85d5b
+F src/btree.c b0b6238c54302e779aa9cb958c8c6da7cab4a090
 F src/btree.h 861e40b759a195ba63819740e484390012cf81ab
 F src/build.c a95eb1181247368b0ffe2eed121a43735976a964
 F src/date.c 4fd4e90b3880dacd67305e96330940dc243ffc10
@@ -101,6 +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 eee59cfd3a0151adbc7d85e5373f31ae6d4c3061
 F test/capi2.test 1ec97bf8896185aec2366c7d07b01edef6ae4b7e
 F test/capi3.test 9258ca75fc98d89477015dcd70aa3d2757b142b8
 F test/capi3b.test 5b6a66f9f295f79f443b5d3f33187fa5ef6cf336
@@ -161,7 +162,7 @@ F test/pagesize.test 6f94b70ed9645dbe6314b627ae765c5dec8036d9
 F test/pragma.test d14ed14407a00c10562ca35128120dc09019ed34
 F test/printf.test 92ba4c510b4fc61120ffa4a01820446ed917ae57
 F test/progress.test 5ddba78cb6011fba36093973cfb3ac473b8fb96a x
-F test/quick.test 9e968949a20b5ed5990c03dc45df3781a03c4b1a
+F test/quick.test 91e5b8ae6663dc9e3e754b271f0384f0cae706e6
 F test/quote.test 6d75cf635d93ba2484dc9cb378d88cbae9dc2c62
 F test/reindex.test 3552c6b944a3fab28cfd3049c04c65cb79419757
 F test/rollback.test 4097328d44510277244ef4fa51b22b2f11d7ef4c
@@ -257,7 +258,7 @@ F www/tclsqlite.tcl 560ecd6a916b320e59f2917317398f3d59b7cc25
 F www/vdbe.tcl 095f106d93875c94b47367384ebc870517431618
 F www/version3.tcl 092a01f5ef430d2c4acc0ae558d74c4bb89638a0
 F www/whentouse.tcl fdacb0ba2d39831e8a6240d05a490026ad4c4e4c
-P 10c712a21961dbc3bff89c49d5ec3b84b9187c80
-R 6f76dd8333301216fe464e4f92eb6cb4
-U drh
-Z da59a89dc62ed0410dba6254f89f1869
+P 60ace9985d4ea2ef0fd245027fab2fffc32014b6
+R 89745b387528d396eaba852b9e2401a3
+U danielk1977
+Z ed00ed3361cf0f8eb48b7fc82890652e
index 720007fefe00e5702a900d3f4f76467cc48a7e43..6326b69aeab7ca88b692cbbbed7388c2e70144d4 100644 (file)
@@ -1 +1 @@
-60ace9985d4ea2ef0fd245027fab2fffc32014b6
\ No newline at end of file
+5eaa18d43f2996a9d354bb4fb9c81e267e98be29
\ No newline at end of file
index 2e086b2f90e1661f8126566fb61b592380d2d1d9..b132302523002539afb770c48b25024e80001ea7 100644 (file)
@@ -9,7 +9,7 @@
 **    May you share freely, never taking more than you give.
 **
 *************************************************************************
-** $Id: btree.c,v 1.216 2004/11/10 11:55:11 danielk1977 Exp $
+** $Id: btree.c,v 1.217 2004/11/13 13:19:56 danielk1977 Exp $
 **
 ** This file implements a external (disk-based) database using BTrees.
 ** For a detailed discussion of BTrees, refer to
@@ -3613,6 +3613,7 @@ static int balance_nonroot(MemPage *pPage){
   int *szCell;                 /* Local size of all cells in apCell[] */
   u8 *aCopy[NB];               /* Space for holding data of apCopy[] */
   u8 *aSpace;                  /* Space to hold copies of dividers cells */
+  BtCursor *pCur;
 
   /* 
   ** Find the parent page.
@@ -3861,16 +3862,6 @@ static int balance_nonroot(MemPage *pPage){
     zeroPage(pNew, pageFlags);
   }
 
-  /* Free any old pages that were not reused as new pages.
-  */
-  while( i<nOld ){
-    rc = freePage(apOld[i]);
-    if( rc ) goto balance_cleanup;
-    releasePage(apOld[i]);
-    apOld[i] = 0;
-    i++;
-  }
-
   /*
   ** Put the new pages in accending order.  This helps to
   ** keep entries in the disk file in order so that a scan
@@ -3915,6 +3906,131 @@ static int balance_nonroot(MemPage *pPage){
     nNew>=4 ? pgnoNew[3] : 0, nNew>=4 ? szNew[3] : 0,
     nNew>=5 ? pgnoNew[4] : 0, nNew>=5 ? szNew[4] : 0));
 
+#if 0
+  /* The following block shows how cells migrated during the balance op. */
+  if( sqlite3_btree_trace ){
+    char zBuf[200];
+    char *zCsr = zBuf;
+    int a, b, c=0, d=0;
+    *zCsr = '\0';
+    for(a=0; a<nOld; a++){
+      int nOldCells = apCopy[a]->nCell+apCopy[a]->nOverflow;
+      for(b=0; b<(nOldCells+((a!=nOld-1&&!leafData)?1:0)); b++){
+        int x = 0;
+        Pgno iNewPage;
+        Pgno iOldPage;
+        int iNewIndex;
+        int iOldIndex;
+
+        if( b<nOldCells ){
+          iOldPage = pgnoOld[a];
+          iOldIndex = b;
+        }else{
+          iOldPage = pParent->pgno;
+          iOldIndex = idxDiv[a];
+        }
+
+        while( cntNew[x]<=c ) x++;
+        if( x>0 && c==cntNew[x-1] && !leafData ){
+          iNewPage = pParent->pgno;
+          iNewIndex = nxDiv + a;
+        }else{
+          assert( x<nNew );
+          iNewPage = pgnoNew[x];
+          iNewIndex = c-(x>0?cntNew[x-1]:0)-(leafData?0:1);
+        }
+
+        if( (&zBuf[sizeof(zBuf)])-zCsr > 100 && 
+            (1 || iOldPage!=iNewPage || iOldIndex!=iNewIndex) ){
+          zCsr += sprintf(zCsr, " %d.%d->%d.%d", iOldPage, iOldIndex,
+              iNewPage, iNewIndex);
+        }
+        c++;
+        if( (d==0 && strlen(zBuf)>35) || strlen(zBuf)>60 ){
+          TRACE(("%s%s\n", d==0?"BALANCE: Cell migration:":"", zBuf));
+          zCsr = zBuf;
+          d = 1;
+        }
+      }
+    }
+    assert( c==nCell );
+    if( zCsr!=zBuf ){
+      TRACE(("%s%s\n", d==0?"BALANCE: Cell migration":"", zBuf));
+    }
+  }
+#endif
+
+  /* If there are other cursors that refer to one of the pages involved
+  ** in the balancing, then adjust these cursors so that they still
+  ** point to the same cells.
+  */
+  for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
+    int nCellCnt = 0;
+    int iCell = -1;
+    Pgno pgno = pCur->pPage->pgno;
+
+    /* If the cursor is not valid, do not do anything with it. */
+    if( !pCur->isValid ) continue;
+
+    for(i=0; iCell<0 && i<nOld; i++){
+      if( pgno==apCopy[i]->pgno ){
+        iCell = nCellCnt + pCur->idx;
+        break;
+      }
+      nCellCnt += (apCopy[i]->nCell + apCopy[i]->nOverflow) + (leafData?0:1);
+    }
+
+    if( pgno==pParent->pgno ){
+      assert( !leafData );
+      assert( iCell==-1 );
+      if( pCur->idx>=nxDiv && pCur->idx<(nxDiv+nOld-1) ){
+        for(i=0; i<=(pCur->idx-nxDiv); i++){
+          iCell += (apCopy[i]->nCell + apCopy[i]->nOverflow + 1);
+        }
+      }
+      if( pCur->idx>=(nxDiv+nOld) ){
+        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;
+      }
+    }
+
+    if( iCell>=0 ){
+      int idxNew;
+      Pgno pgnoNew;
+      int x = 0;
+
+      while( cntNew[x]<=iCell ) x++;
+      if( x>0 && !leafData && cntNew[x-1]==iCell ){
+        /* The cell that pCur points to is a divider cell in pParent. */
+        pgnoNew = pParent->pgno;
+        idxNew = nxDiv + x-1;
+      }else{
+        /* The cell that pCur points to is on page apNew[x]. */
+        idxNew = iCell-(x>0?cntNew[x-1]:0)-((leafData||x==0)?0:1);
+        pgnoNew = apNew[x]->pgno;
+      }
+
+      TRACE(("BALANCE: Cursor %p migrates from %d,%d to %d,%d\n", 
+          pCur, pgno, pCur->idx, pgnoNew, idxNew));
+
+      pCur->idx = idxNew;
+      releasePage(pCur->pPage);
+      rc = getPage(pBt, pgnoNew, &pCur->pPage);
+      assert( rc==SQLITE_OK );
+      pCur->info.nSize = 0;
+    }
+  }
+
+  /* Free any old pages that were not reused as new pages.
+  */
+  for(i=nNew; i<nOld; i++){
+    rc = freePage(apOld[i]);
+    if( rc ) goto balance_cleanup;
+    releasePage(apOld[i]);
+    apOld[i] = 0;
+  }
 
   /*
   ** Evenly distribute the data in apCell[] across the new pages.
@@ -4111,6 +4227,7 @@ static int balance_deeper(MemPage *pPage){
   u8 *cdata;          /* Content of the child page */
   int hdr;            /* Offset to page header in parent */
   int brk;            /* Offset to content of first cell in parent */
+  BtCursor *pCur;
 
   assert( pPage->pParent==0 );
   assert( pPage->nOverflow>0 );
@@ -4136,6 +4253,21 @@ static int balance_deeper(MemPage *pPage){
   zeroPage(pPage, pChild->aData[0] & ~PTF_LEAF);
   put4byte(&pPage->aData[pPage->hdrOffset+8], pgnoChild);
   TRACE(("BALANCE: copy root %d into %d\n", pPage->pgno, pChild->pgno));
+
+  /* If there were cursors pointing at this page, point them at the new
+  ** page instead. Decrement the reference count for the old page and
+  ** increment it for the new one.
+  */
+  for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
+    if( pCur->pPage==pPage ){
+      TRACE(("BALANCE: Cursor %p migrates from %d,%d to %d,%d\n", 
+          pCur, pPage->pgno, pCur->idx, pChild->pgno, pCur->idx));
+      releasePage(pCur->pPage);
+      rc = getPage(pBt, pChild->pgno,  &pCur->pPage);
+      assert( rc==SQLITE_OK );
+    }
+  }
+
   rc = balance_nonroot(pChild);
   releasePage(pChild);
   return rc;
@@ -4183,7 +4315,7 @@ static int checkReadLocks(Btree *pBt, Pgno pgnoRoot, BtCursor *pExclude){
     if( p->pgnoRoot!=pgnoRoot || p==pExclude ) continue;
     if( p->wrFlag==0 ) return SQLITE_LOCKED;
     if( p->pPage->pgno!=p->pgnoRoot ){
-      moveToRoot(p);
+/*      moveToRoot(p); */
     }
   }
   return SQLITE_OK;
@@ -4210,6 +4342,7 @@ int sqlite3BtreeInsert(
   Btree *pBt = pCur->pBt;
   unsigned char *oldCell;
   unsigned char *newCell = 0;
+  BtCursor *pCur2;
 
   if( pCur->status ){
     return pCur->status;  /* A rollback destroyed this cursor */
@@ -4261,12 +4394,32 @@ int sqlite3BtreeInsert(
     assert( pPage->leaf );
   }
   rc = insertCell(pPage, pCur->idx, newCell, szNew, 0);
+  pCur->isValid = 1;
+
+  /* If there are other cursors pointing at this page with a BtCursor.idx
+  ** field greater than or equal to 'i', then the cell they refer to
+  ** has been modified or moved within the page. Fix the cursor.
+  */
+  for(pCur2=pPage->pBt->pCursor; pCur2; pCur2 = pCur2->pNext){
+    if( pCur2->pPage==pPage ){
+      if( pCur2->idx>=pCur->idx && pCur!=pCur2 && loc!=0 ){
+       /* The cell pointed to by pCur2 was shifted one to the right on it's
+       ** page by this Insert(). 
+        */
+        TRACE(("INSERT: Cursor %p migrates from %d,%d to %d,%d\n", 
+            pCur2, pPage->pgno, pCur2->idx, pPage->pgno, pCur2->idx+1));
+        pCur2->idx++;
+      }
+      pCur2->info.nSize = 0;
+    }
+  }
+
   if( rc!=SQLITE_OK ) goto end_insert;
   rc = balance(pPage);
   /* sqlite3BtreePageDump(pCur->pBt, pCur->pgnoRoot, 1); */
   /* fflush(stdout); */
   if( rc==SQLITE_OK ){
-    moveToRoot(pCur);
+  /*  moveToRoot(pCur); */
   }
 end_insert:
   sqliteFree(newCell);
@@ -4350,6 +4503,7 @@ int sqlite3BtreeDelete(BtCursor *pCur){
     rc = insertCell(pPage, pCur->idx, pNext-4, szNext+4, tempCell);
     if( rc!=SQLITE_OK ) return rc;
     put4byte(findOverflowCell(pPage, pCur->idx), pgnoChild);
+    pCur->isValid = 0;
     rc = balance(pPage);
     sqliteFree(tempCell);
     if( rc ) return rc;
@@ -4360,6 +4514,7 @@ int sqlite3BtreeDelete(BtCursor *pCur){
     TRACE(("DELETE: table=%d delete from leaf %d\n",
        pCur->pgnoRoot, pPage->pgno));
     dropCell(pPage, pCur->idx, cellSizePtr(pPage, pCell));
+    pCur->isValid = 0;
     rc = balance(pPage);
   }
   moveToRoot(pCur);
diff --git a/test/btree8.test b/test/btree8.test
new file mode 100644 (file)
index 0000000..33a4049
--- /dev/null
@@ -0,0 +1,151 @@
+# 2004 Jun 4   
+#
+# The author disclaims copyright to this source code.  In place of
+# a legal notice, here is a blessing:
+#
+#    May you do good and not evil.
+#    May you find forgiveness for yourself and forgive others.
+#    May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file implements regression tests for SQLite library.  The
+# focus of this script is btree database backend.
+#
+# $Id: btree8.test,v 1.1 2004/11/13 13:19:56 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Use the SQL interface to create a couple of btree tables, one using
+# the flags for an SQL table, the other an SQL index.
+# 
+do_test btree8-1.0 {
+  execsql {
+    CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
+    CREATE INDEX i1 ON t1(b);
+  }
+} {}
+set tnum [execsql {SELECT rootpage FROM sqlite_master where type = 'table'}]
+set inum [execsql {SELECT rootpage FROM sqlite_master where type = 'index'}]
+db close
+
+# Open the database at the btree level and begin a transaction
+do_test btree8-1.1 {
+  set ::bt [btree_open test.db 100 0]
+  btree_begin_transaction $::bt
+  expr 0
+} {0}
+
+# For each element in the list $keys, insert an entry into the SQL table
+# with the corresponding key value. Check that the cursor used to insert
+# the key is left pointing to it after the insert. Then save this cursor
+# in the list $csr_list.
+#
+set keys [list 3178 4886 719 1690 443 4113 1618 310 1320 2028]
+set csr_list [list]
+set testnum 2
+foreach key $keys {
+  do_test btree-8-1.$testnum {
+    set csr [btree_cursor $::bt $::tnum 1]
+    btree_insert $csr $key [string repeat a 10]
+    lappend csr_list $csr
+    btree_key $csr
+  } $key
+  incr testnum 
+}
+btree_commit $::bt
+
+# set btree_trace 1
+
+# Now write more entries to the table (and overwriting the ones that exist).
+# After each write, check that the cursors created above still point to the
+# same entries.
+btree_begin_transaction $::bt
+set ::write_csr [btree_cursor $::bt $::tnum 1]
+for {set i $testnum} {$i < 5000 && $nErr==0 } {incr i} {
+  set datalen [expr int(rand()*20.0)]
+
+  do_test btree8-1.$i.1 {
+    btree_insert $::write_csr $i [string repeat x $datalen]
+  } {}
+
+  set testnum 1
+  foreach csr $csr_list key $keys {
+    incr testnum
+    do_test btree8-1.$i.$testnum {
+      btree_key $::csr
+    } $key
+  }
+}
+btree_close_cursor $::write_csr
+btree_commit $::bt
+if {$::nErr>0} { puts $::csr_list }
+foreach csr $csr_list {
+  btree_close_cursor $csr
+}
+set csr_list [list]
+
+# 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:
+#
+# [num_to_string 456 10] -> "456.456.45"
+#
+proc num_to_string {num len} {
+  return [string range [string repeat "$num." $len] 0 [expr $len-1]]
+}
+
+foreach key $keys {
+  lappend skeys [num_to_string $key 20]
+}
+
+# For each element in the list $skeys, insert an entry into the SQL index
+# with the corresponding key value. Check that the cursor used to insert
+# the key is left pointing to it after the insert. Then save this cursor
+# in the list $csr_list.
+#
+btree_begin_transaction $::bt
+set testnum 0
+foreach key $skeys {
+  incr testnum 
+  do_test btree-8-2.$testnum {
+    set csr [btree_cursor $::bt $::inum 1]
+    btree_insert $csr $key ""
+    lappend csr_list $csr
+    btree_key $csr
+  } $key
+}
+btree_commit $::bt
+
+# set btree_trace 1
+
+# Now write more entries to the index (and overwrite the ones that exist).
+# After each write, check that the cursors created above still point to the
+# same entries.
+btree_begin_transaction $::bt
+set ::write_csr [btree_cursor $::bt $::inum 1]
+for {set i $testnum} {$i < 5000 && $nErr==0 } {incr i} {
+  set skey [num_to_string $i 20]
+
+  do_test btree8-2.$i.1 {
+    btree_insert $::write_csr $skey ""
+  } {}
+
+  set testnum 1
+  foreach csr $csr_list key $skeys {
+    incr testnum
+    do_test btree8-2.$i.$testnum {
+      btree_key $::csr
+    } $key
+  }
+}
+btree_close_cursor $::write_csr
+btree_commit $::bt
+if {$::nErr>0} { puts $::csr_list }
+foreach csr $csr_list {
+  btree_close_cursor $csr
+}
+set csr_list [list]
+
+finish_test
+
index 74c01c0dc40a78440e5f31fa9716ce243d276254..8cc30cfea0f685eb51899f2dc4bfd1308c13a5c8 100644 (file)
@@ -10,7 +10,7 @@
 #***********************************************************************
 # This file runs all tests.
 #
-# $Id: quick.test,v 1.32 2004/11/08 09:26:10 danielk1977 Exp $
+# $Id: quick.test,v 1.33 2004/11/13 13:19:56 danielk1977 Exp $
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
@@ -33,6 +33,7 @@ set EXCLUDE {
   quick.test
   utf16.test
   autovacuum_crash.test
+  btree8.test
 }
 
 if {[sqlite3 -has-codec]} {