]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
When a transaction or savepoint rollback occurs, save the positions of all open read...
authordan <dan@noemail.net>
Wed, 12 Nov 2014 14:56:02 +0000 (14:56 +0000)
committerdan <dan@noemail.net>
Wed, 12 Nov 2014 14:56:02 +0000 (14:56 +0000)
FossilOrigin-Name: dd03a2802f3f276525f3cef9a93f825dd8606626

manifest
manifest.uuid
src/btree.c
src/btree.h
src/vdbe.c
test/rollback2.test [new file with mode: 0644]

index 97192e581d50beda02d6200e560dc45e1060fa4e..11b9906fa07f7253b1afe4d5d1ab48d0e3cac2e3 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Remove\ssome\scalls\sto\sthe\s'breakpoint'\stest\scommand.
-D 2014-11-11T19:07:56.372
+C When\sa\stransaction\sor\ssavepoint\srollback\soccurs,\ssave\sthe\spositions\sof\sall\sopen\sread-cursors\sso\sthat\sthey\scan\sbe\srestored\sfollowing\sthe\srollback\soperation.
+D 2014-11-12T14:56:02.923
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in a226317fdf3f4c895fb3cfedc355b4d0868ce1fb
 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -173,8 +173,8 @@ F src/auth.c b56c78ebe40a2110fd361379f7e8162d23f92240
 F src/backup.c 86b311af1ad35decd1f2f0cee457d344acb83c3b
 F src/bitvec.c 19a4ba637bd85f8f63fc8c9bae5ade9fb05ec1cb
 F src/btmutex.c 49ca66250c7dfa844a4d4cb8272b87420d27d3a5
-F src/btree.c eaef0003bbfe740c62189355dabc818fc3a98999
-F src/btree.h d24fc2f3bc53be220b79b95800bdb2ee207b1089
+F src/btree.c d5d991b518fa5bebc64037dfeb98a48051d864d7
+F src/btree.h e31a3a3ebdedb1caf9bda3ad5dbab3db9b780f6e
 F src/btreeInt.h 026d0129724e8f265fdc60d44ec240cf5a4e6179
 F src/build.c 67bb05b1077e0cdaccb2e36bfcbe7a5df9ed31e8
 F src/callback.c 7b44ce59674338ad48b0e84e7b72f935ea4f68b0
@@ -291,7 +291,7 @@ F src/update.c 3c4ecc282accf12d39edb8d524cf089645e55a13
 F src/utf.c fc6b889ba0779b7722634cdeaa25f1930d93820c
 F src/util.c 3b627daa45c7308c1e36e3dbaa3f9ce7e5c7fa73
 F src/vacuum.c 9b30ec729337dd012ed88d4c292922c8ef9cf00c
-F src/vdbe.c c71d819bb34269c3dbccd92e6bb308f0ec025b5d
+F src/vdbe.c 5e47308836e9bb5fdb4835fdf88eeab071848d3f
 F src/vdbe.h 6fc69d9c5e146302c56e163cb4b31d1ee64a18c3
 F src/vdbeInt.h 9bb69ff2447c34b6ccc58b34ec35b615f86ead78
 F src/vdbeapi.c 07acb615d1e4170e71fc1b0d087f3c53a1ad8e83
@@ -797,6 +797,7 @@ F test/releasetest.mk 2eced2f9ae701fd0a29e714a241760503ccba25a
 F test/releasetest.tcl a4279c890698584feb2ffc86735857a4e4474180
 F test/resolver01.test 33abf37ff8335e6bf98f2b45a0af3e06996ccd9a
 F test/rollback.test 458fe73eb3ffdfdf9f6ba3e9b7350a6220414dea
+F test/rollback2.test 552abaab8e721b6060a727d639896427059e51ec
 F test/rowhash.test 0bc1d31415e4575d10cacf31e1a66b5cc0f8be81
 F test/rowid.test 742b5741584a8a44fd83e856cc2896688401d645
 F test/rtree.test 0c8d9dd458d6824e59683c19ab2ffa9ef946f798
@@ -1218,7 +1219,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
 F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
-P a066a3832a7c6de65c3016e77e49ac00e09db749
-R e8fc41a7f82a02ab8618a341f7ef7735
-U mistachkin
-Z 90417f4c358533080e79b8017b4e7453
+P 1412fcc480799ecbd68d44dd18d5bad40e20ccf1
+R 668a86f911283b47b6c156fecf772681
+U dan
+Z cf8e25329461439611751d7913ef3ef9
index f90bf2d8c82b1326d86e49a881630d5e55c30732..3369a7ad97a2d71a61d03dbcce776b44cc7aa1ca 100644 (file)
@@ -1 +1 @@
-1412fcc480799ecbd68d44dd18d5bad40e20ccf1
\ No newline at end of file
+dd03a2802f3f276525f3cef9a93f825dd8606626
\ No newline at end of file
index 70fae7c7077677f69e096599e06dd138a265bcac..f153a0cc45d58a30cb9f03a4ffb9fd907cb4e6ba 100644 (file)
@@ -3514,29 +3514,52 @@ int sqlite3BtreeCommit(Btree *p){
 ** that belong to other database connections that happen to be
 ** sharing the cache with pBtree.
 **
-** This routine gets called when a rollback occurs.  The writeOnly
-** flag is set to 1 if the transaction did not make any schema
-** changes, in which case the read cursors can continue operating.
-** If schema changes did occur in the transaction, then both read
-** and write cursors must both be tripped.
-*/
-void sqlite3BtreeTripAllCursors(Btree *pBtree, int errCode, int writeOnly){
+** This routine gets called when a rollback occurs. If the writeOnly
+** flag is true, then only write-cursors need be tripped - read-only
+** cursors save their current positions so that they may continue 
+** following the rollback. Or, if writeOnly is false, all cursors are 
+** tripped. In general, writeOnly is false if the transaction being
+** rolled back modified the database schema. In this case b-tree root
+** pages may be moved or deleted from the database altogether, making
+** it unsafe for read cursors to continue.
+**
+** If the writeOnly flag is true and an error is encountered while 
+** saving the current position of a read-only cursor, all cursors, 
+** including all read-cursors are tripped.
+**
+** SQLITE_OK is returned if successful, or if an error occurs while
+** saving a cursor position, an SQLite error code.
+*/
+int sqlite3BtreeTripAllCursors(Btree *pBtree, int errCode, int writeOnly){
   BtCursor *p;
+  int rc = SQLITE_OK;
+
   assert( (writeOnly==0 || writeOnly==1) && BTCF_WriteFlag==1 );
-  if( pBtree==0 ) return;
-  sqlite3BtreeEnter(pBtree);
-  for(p=pBtree->pBt->pCursor; p; p=p->pNext){
-    int i;
-    if( writeOnly && (p->curFlags & BTCF_WriteFlag)==0 ) continue;
-    sqlite3BtreeClearCursor(p);
-    p->eState = CURSOR_FAULT;
-    p->skipNext = errCode;
-    for(i=0; i<=p->iPage; i++){
-      releasePage(p->apPage[i]);
-      p->apPage[i] = 0;
+  if( pBtree ){
+    sqlite3BtreeEnter(pBtree);
+    for(p=pBtree->pBt->pCursor; p; p=p->pNext){
+      int i;
+      if( writeOnly && (p->curFlags & BTCF_WriteFlag)==0 ){
+        if( p->eState==CURSOR_VALID ){
+          int rc = saveCursorPosition(p);
+          if( rc!=SQLITE_OK ){
+            (void)sqlite3BtreeTripAllCursors(pBtree, rc, 0);
+            break;
+          }
+        }
+      }else{
+        sqlite3BtreeClearCursor(p);
+        p->eState = CURSOR_FAULT;
+        p->skipNext = errCode;
+      }
+      for(i=0; i<=p->iPage; i++){
+        releasePage(p->apPage[i]);
+        p->apPage[i] = 0;
+      }
     }
+    sqlite3BtreeLeave(pBtree);
   }
-  sqlite3BtreeLeave(pBtree);
+  return rc;
 }
 
 /*
@@ -3565,7 +3588,9 @@ int sqlite3BtreeRollback(Btree *p, int tripCode, int writeOnly){
     rc = SQLITE_OK;
   }
   if( tripCode ){
-    sqlite3BtreeTripAllCursors(p, tripCode, writeOnly);
+    int rc2 = sqlite3BtreeTripAllCursors(p, tripCode, writeOnly);
+    assert( rc==SQLITE_OK || (writeOnly==0 && rc2==SQLITE_OK) );
+    if( rc2!=SQLITE_OK ) rc = rc2;
   }
   btreeIntegrity(p);
 
index a6093a5edd0c4a616577cb080e75038344842d53..281e57dafd87452842dc81060e1aa5bab858321c 100644 (file)
@@ -116,7 +116,7 @@ int sqlite3BtreeIncrVacuum(Btree *);
 int sqlite3BtreeDropTable(Btree*, int, int*);
 int sqlite3BtreeClearTable(Btree*, int, int*);
 int sqlite3BtreeClearTableOfCursor(BtCursor*);
-void sqlite3BtreeTripAllCursors(Btree*, int, int);
+int sqlite3BtreeTripAllCursors(Btree*, int, int);
 
 void sqlite3BtreeGetMeta(Btree *pBtree, int idx, u32 *pValue);
 int sqlite3BtreeUpdateMeta(Btree*, int idx, u32 value);
index 715862e73cda110881c410baf6fff86ea44ad8f3..e4cbeb6565a3bf34af74c09129b9b2576f5f445c 100644 (file)
@@ -2830,8 +2830,9 @@ case OP_Savepoint: {
         if( p1==SAVEPOINT_ROLLBACK ){
           isSchemaChange = (db->flags & SQLITE_InternChanges)!=0;
           for(ii=0; ii<db->nDb; ii++){
-            sqlite3BtreeTripAllCursors(db->aDb[ii].pBt, SQLITE_ABORT,
+            rc = sqlite3BtreeTripAllCursors(db->aDb[ii].pBt, SQLITE_ABORT,
                                        isSchemaChange==0);
+            if( rc!=SQLITE_OK ) goto abort_due_to_error;
           }
         }else{
           isSchemaChange = 0;
diff --git a/test/rollback2.test b/test/rollback2.test
new file mode 100644 (file)
index 0000000..9637f0c
--- /dev/null
@@ -0,0 +1,74 @@
+# 2014 November 12
+#
+# 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.
+#
+#***********************************************************************
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set ::testprefix rollback2
+
+proc int2hex {i} { format %.2X $i }
+db func int2hex int2hex
+
+do_execsql_test 1.0 {
+  SELECT int2hex(0), int2hex(100), int2hex(255)
+} {00 64 FF}
+do_execsql_test 1.1 {
+  CREATE TABLE t1(i, h);
+  CREATE INDEX i1 ON t1(h);
+  WITH data(a, b) AS (
+    SELECT 1, int2hex(1)
+      UNION ALL
+    SELECT a+1, int2hex(a+1) FROM data WHERE a<40
+  )
+  INSERT INTO t1 SELECT * FROM data;
+} {}
+
+
+proc do_rollback_test {tn args} {
+  set A(-setup)    ""
+  set A(-select)   ""
+  set A(-result)   ""
+  set A(-rollback) ROLLBACK
+
+  array set O $args
+  foreach k [array names O] {
+    if {[info exists A($k)]==0} { error "unknown option: $k" }
+    set A($k) $O($k)
+  }
+
+  for {set iRollback 0} 1 {incr iRollback} {
+    catch { db eval ROLLBACK }
+    set res [list]
+    db eval $A(-setup)
+
+    set i 0
+    db eval $A(-select) x {
+      if {$i==$iRollback} { db eval $A(-rollback) }
+      foreach k $x(*) { lappend res $x($k) }
+      incr i
+    }
+
+    do_test $tn.$iRollback [list set {} $res] [list {*}$A(-result)]
+    if {$i < $iRollback} break
+  }
+}
+
+do_rollback_test 2 -setup {
+  BEGIN;
+    DELETE FROM t1 WHERE (i%2)==1;
+} -select {
+  SELECT i FROM t1 WHERE (i%2)==0
+} -result {
+  2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40
+}
+
+finish_test
+