]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Make calls to sqlite3BtreeRollbackStmt() no-ops when passed a Btree* handle that...
authordanielk1977 <danielk1977@noemail.net>
Thu, 12 Mar 2009 14:43:27 +0000 (14:43 +0000)
committerdanielk1977 <danielk1977@noemail.net>
Thu, 12 Mar 2009 14:43:27 +0000 (14:43 +0000)
FossilOrigin-Name: a1bb1aef0e06140a2d5d5e4b6c10c73ce95c89e0

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

index 05d08a2478309fb4d103b9a838d453b065edbef6..74d28a87620e970acc4156a03ba318569341b47d 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Begin\spurging\sdirty\spages\sfrom\sthe\scache\sonce\s90%\sof\sthe\scache\sis\sdirty\s(insteadof\swaiting\suntil\sit\sis\s100%\sdirty).\sThis\simproves\sperformance\sin\ssome\scircumstances\sby\seffectively\sreserving\s10%\sof\sthe\sconfigured\spage-cache\sfor\sfrequently\sreused\sread-only\spages.\s(CVS\s6341)
-D 2009-03-05T14:59:40
+C Make\scalls\sto\ssqlite3BtreeRollbackStmt()\sno-ops\swhen\spassed\sa\sBtree*\shandle\sthat\sdoes\snot\shave\san\sopen\sstatement\stransaction.\sTicket\s#3718.\s(CVS\s6342)
+D 2009-03-12T14:43:28
 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0
 F Makefile.in d64baddbf55cdf33ff030e14da837324711a4ef7
 F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
@@ -103,7 +103,7 @@ F src/auth.c c8b2ab5c8bad4bd90ed7c294694f48269162c627
 F src/backup.c 2d3f31148d7b086c5c72d9edcd04fc2751b0aa6e
 F src/bitvec.c 44f7059ac1f874d364b34af31b9617e52223ba75
 F src/btmutex.c 341502bc496dc0840dcb00cde65680fb0e85c3ab
-F src/btree.c ec710abc5a71eefba93d2b99330ff2eacd941e08
+F src/btree.c 6e7501d7a207dcc15b099e67231bc8cc86ef7fe9
 F src/btree.h 96a019c9f28da38e79940512d7800e419cd8c702
 F src/btreeInt.h 0a4884e6152d7cae9c741e91b830064c19fd2c05
 F src/build.c 741240c8d6a54201fa8757db1ee6efba71be59a2
@@ -493,7 +493,7 @@ F test/pragma2.test 5364893491b9231dd170e3459bfc2e2342658b47
 F test/printf.test 47e9e5bbec8509023479d54ceb71c9d05a95308a
 F test/progress.test 5b075c3c790c7b2a61419bc199db87aaf48b8301 x
 F test/ptrchng.test ef1aa72d6cf35a2bbd0869a649b744e9d84977fc
-F test/quick.test b7acd5b3df583391979d9f9edf98aa85fc95a3f6
+F test/quick.test d93ab4f1eee87b89fddbe938e2f093ce33e7b46a
 F test/quote.test 215897dbe8de1a6f701265836d6601cc6ed103e6
 F test/randexpr1.tcl 40dec52119ed3a2b8b2a773bce24b63a3a746459
 F test/randexpr1.test 1084050991e9ba22c1c10edd8d84673b501cc25a
@@ -556,8 +556,9 @@ F test/temptrigger.test 03093be9967942623232dfdf2a63b832d4e0e4fa
 F test/tester.tcl 66546f6766029384360b24cacb3896376c5f5f69
 F test/thread001.test 06c45ed9597d478e7bbdc2a8937e1ebea2a20a32
 F test/thread002.test 3c03900f03fd2fe8e2fbb1bbdef7fa8206fdb7ad
-F test/thread003.test 6d360c15afe7f6ef6186801d2cb8407bccbe3aa3
+F test/thread003.test a8bc91af1d9d524148dd84e4d6a196ba17521e08
 F test/thread004.test 9d8ea6a9b0d62d35ad0b967e010d723ed99f614a
+F test/thread005.test 5141b3ee8debc99549f62512265a50be36d1b6a6
 F test/thread1.test 862dd006d189e8b0946935db17399dcac2f8ef91
 F test/thread2.test 91f105374f18a66e73a3254c28fe7c77af69bdea
 F test/thread_common.tcl 047f80288b5e1e86bed181097d67e640f1a54a74
@@ -703,7 +704,7 @@ F tool/speedtest16.c c8a9c793df96db7e4933f0852abb7a03d48f2e81
 F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
 F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
-P d0b2015f1caf2fc60ec82bd8e760f7b61befa3b4
-R 039bb14c767370b62d0edeb68de61ac0
+P 823fe7f5551e121e211d1ede606a7ce7487ffe0d
+R 0a620faaa60e0f8f26633feb77199541
 U danielk1977
-Z 7a03629d714ccf300df7a819fca83b05
+Z e6301158e9a3d6dc90886b4a779a1ded
index 85f52c709677a02b297c23bd75953206a59b79cc..e9b0ea00faeaf9775135e54304f2a029aa4ca8e3 100644 (file)
@@ -1 +1 @@
-823fe7f5551e121e211d1ede606a7ce7487ffe0d
\ No newline at end of file
+a1bb1aef0e06140a2d5d5e4b6c10c73ce95c89e0
\ No newline at end of file
index 9729ec96e82f2a4deb03893729bd8623e0d72e42..b012e51598c114126a7c7756aa236440c991cc70 100644 (file)
@@ -9,7 +9,7 @@
 **    May you share freely, never taking more than you give.
 **
 *************************************************************************
-** $Id: btree.c,v 1.571 2009/03/05 04:20:32 shane Exp $
+** $Id: btree.c,v 1.572 2009/03/12 14:43:28 danielk1977 Exp $
 **
 ** This file implements a external (disk-based) database using BTrees.
 ** See the header comment on "btreeInt.h" for additional information.
@@ -2822,18 +2822,16 @@ int sqlite3BtreeBeginStmt(Btree *p){
 ** subtransaction is active, this is a no-op.
 */
 int sqlite3BtreeCommitStmt(Btree *p){
-  int rc;
+  int rc = SQLITE_OK;
   BtShared *pBt = p->pBt;
   sqlite3BtreeEnter(p);
   pBt->db = p->db;
-  assert( pBt->readOnly==0 );
-  if( pBt->inStmt ){
+  if( p->inTrans==TRANS_WRITE && pBt->inStmt ){
     int iStmtpoint = p->db->nSavepoint;
+    assert( pBt->readOnly==0 );
     rc = sqlite3PagerSavepoint(pBt->pPager, SAVEPOINT_RELEASE, iStmtpoint);
-  }else{
-    rc = SQLITE_OK;
+    pBt->inStmt = 0;
   }
-  pBt->inStmt = 0;
   sqlite3BtreeLeave(p);
   return rc;
 }
@@ -2851,9 +2849,9 @@ int sqlite3BtreeRollbackStmt(Btree *p){
   BtShared *pBt = p->pBt;
   sqlite3BtreeEnter(p);
   pBt->db = p->db;
-  assert( pBt->readOnly==0 );
-  if( pBt->inStmt ){
+  if( p->inTrans==TRANS_WRITE && pBt->inStmt ){
     int iStmtpoint = p->db->nSavepoint;
+    assert( pBt->readOnly==0 );
     rc = sqlite3PagerSavepoint(pBt->pPager, SAVEPOINT_ROLLBACK, iStmtpoint);
     if( rc==SQLITE_OK ){
       rc = sqlite3PagerSavepoint(pBt->pPager, SAVEPOINT_RELEASE, iStmtpoint);
index 4d8dca6e8a5bf9b0890f8dda8ab3a553eb5bdeb8..11e6beffdaeb994a01d8cf10d66ea14a155c7322 100644 (file)
@@ -6,7 +6,7 @@
 #***********************************************************************
 # This file runs all tests.
 #
-# $Id: quick.test,v 1.93 2009/02/26 07:15:59 danielk1977 Exp $
+# $Id: quick.test,v 1.94 2009/03/12 14:43:28 danielk1977 Exp $
 
 proc lshift {lvar} {
   upvar $lvar l
@@ -86,6 +86,7 @@ set EXCLUDE {
   thread002.test
   thread003.test
   thread004.test
+  thread005.test
   trans2.test
   vacuum3.test
 
index 916fc7e06db9ea97cebc009533c37637c156d0af..61841a01bdaf308e2482da53409960cc07445fd6 100644 (file)
@@ -12,7 +12,7 @@
 #   This file contains tests that attempt to break the pcache module
 #   by bombarding it with simultaneous requests from multiple threads.
 #     
-# $Id: thread003.test,v 1.6 2009/02/12 17:06:41 drh Exp $
+# $Id: thread003.test,v 1.7 2009/03/12 14:43:28 danielk1977 Exp $
 
 set testdir [file dirname $argv0]
 
@@ -158,7 +158,7 @@ do_test thread003.3 {
 # sqlite3_release_memory() over and over again.
 #
 set nSecond 30
-puts "Starting thread003.3 (should run for ~$nSecond seconds)"
+puts "Starting thread003.4 (should run for ~$nSecond seconds)"
 unset -nocomplain finished(1)
 unset -nocomplain finished(2)
 do_test thread003.4 {
diff --git a/test/thread005.test b/test/thread005.test
new file mode 100644 (file)
index 0000000..6bd358d
--- /dev/null
@@ -0,0 +1,174 @@
+# 2009 March 11
+#
+# 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.
+#
+#***********************************************************************
+#
+# Test a race-condition that shows up in shared-cache mode.
+#
+# $Id: thread005.test,v 1.1 2009/03/12 14:43:28 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+
+source $testdir/tester.tcl
+ifcapable !mutex||!shared_cache {
+  return
+}
+source $testdir/thread_common.tcl
+if {[info commands sqlthread] eq ""} {
+  return
+}
+
+#-------------------------------------------------------------------------
+# This test tries to exercise a race-condition that existed in shared-cache
+# mode at one point. The test uses two threads; each has a database connection
+# open on the same shared cache. The schema of the database is:
+#
+#    CREATE TABLE t1(a INTEGER PRIMARY KEY, b UNIQUE);
+#
+# One thread is a reader and the other thread a reader and a writer. The 
+# writer thread repeats the following transaction as fast as possible:
+# 
+#      BEGIN;
+#        DELETE FROM t1 WHERE a = (SELECT max(a) FROM t1);
+#        INSERT INTO t1 VALUES(NULL, NULL);
+#        UPDATE t1 SET b = a WHERE a = (SELECT max(a) FROM t1);
+#        SELECT count(*) FROM t1 WHERE b IS NULL;
+#      COMMIT;
+#
+# The reader thread does the following over and over as fast as possible:
+#
+#      BEGIN;
+#        SELECT count(*) FROM t1 WHERE b IS NULL;
+#      COMMIT;
+#
+# The test runs for 20 seconds or until one of the "SELECT count(*)" 
+# statements returns a non-zero value. If an SQLITE_LOCKED error occurs,
+# the connection issues a ROLLBACK immediately to abandon the current
+# transaction.
+#
+# If everything is working correctly, the "SELECT count(*)" statements 
+# should never return a value other than 0. The "INSERT" statement 
+# executed by the writer adds a row with "b IS NULL" to the table, but
+# the subsequent UPDATE statement sets its "b" value to an integer
+# immediately afterwards.
+#
+# However, before the race-condition was fixed, if the reader's SELECT
+# statement hit an error (say an SQLITE_LOCKED) at the same time as the
+# writer was executing the UPDATE statement, then it could incorrectly
+# rollback the statement-transaction belonging to the UPDATE statement.
+# The UPDATE statement would still be reported as successful to the user,
+# but it would have no effect on the database contents.
+# 
+# Note that it has so far only proved possible to hit this race-condition
+# when using an ATTACHed database. There doesn't seem to be any reason
+# for this, other than that operating on an ATTACHed database means there
+# are a few more mutex grabs and releases during the window of time open
+# for the race-condition. Maybe this encourages the scheduler to context
+# switch or something...
+#
+
+# Use shared-cache mode for this test.
+# 
+db close
+set ::enable_shared_cache [sqlite3_enable_shared_cache]
+sqlite3_enable_shared_cache 1
+
+file delete -force test.db test2.db
+
+do_test thread005-1.1 {
+  sqlite3 db test.db
+  execsql { ATTACH 'test2.db' AS aux }
+  execsql {
+    CREATE TABLE aux.t1(a INTEGER PRIMARY KEY, b UNIQUE);
+    INSERT INTO t1 VALUES(1, 1);
+    INSERT INTO t1 VALUES(2, 2);
+  }
+  db close
+} {}
+
+set ThreadProgram {
+  proc execsql {zSql {db {}}} {
+    if {$db eq ""} {set db $::DB}
+
+    set lRes [list]
+    set rc SQLITE_OK
+
+    while {$rc=="SQLITE_OK" && $zSql ne ""} {
+      set STMT [sqlite3_prepare_v2 $db $zSql -1 zSql]
+      while {[set rc [sqlite3_step $STMT]] eq "SQLITE_ROW"} {
+        for {set i 0} {$i < [sqlite3_column_count $STMT]} {incr i} {
+          lappend lRes [sqlite3_column_text $STMT 0]
+        }
+      }
+      set rc [sqlite3_finalize $STMT]
+    }
+
+    if {$rc != "SQLITE_OK"} { error "$rc [sqlite3_errmsg $db]" }
+    return $lRes
+  }
+
+  if {$isWriter} {
+    set Sql {
+      BEGIN;
+        DELETE FROM t1 WHERE a = (SELECT max(a) FROM t1);
+        INSERT INTO t1 VALUES(NULL, NULL);
+        UPDATE t1 SET b = a WHERE a = (SELECT max(a) FROM t1);
+        SELECT count(*) FROM t1 WHERE b IS NULL;
+      COMMIT;
+    }
+  } else {
+    set Sql {
+      BEGIN;
+      SELECT count(*) FROM t1 WHERE b IS NULL;
+      COMMIT;
+    }
+  }
+
+  set ::DB [sqlite3_open test.db]
+
+  execsql { ATTACH 'test2.db' AS aux }
+
+  set result "ok"
+  set finish [expr [clock_seconds]+5]
+  while {$result eq "ok" && [clock_seconds] < $finish} {
+    set rc [catch {execsql $Sql} msg]
+    if {$rc} {
+      if {[string match "SQLITE_LOCKED*" $msg]} {
+        catch { execsql ROLLBACK }
+      } else {
+        error $msg
+      }
+    } elseif {$msg ne "0"} {
+      set result "failed"
+    }
+  }
+
+  sqlite3_close $::DB
+  set result
+}
+
+puts "Running thread-tests for ~20 seconds"
+thread_spawn finished(0) {set isWriter 0} $ThreadProgram
+thread_spawn finished(1) {set isWriter 1} $ThreadProgram
+if {![info exists finished(0)]} { vwait finished(0) }
+if {![info exists finished(1)]} { vwait finished(1) }
+
+do_test thread005-1.2 {
+  list $finished(0) $finished(1)
+} {ok ok}
+
+do_test thread005-1.3 {
+  sqlite3 db test.db
+  execsql { ATTACH 'test2.db' AS aux }
+  execsql { SELECT count(*) FROM t1 WHERE b IS NULL }
+} {0}
+
+sqlite3_enable_shared_cache $::enable_shared_cache
+finish_test
+