Handle some of the IO error conditions that may occur in a shared-cache context. (CVS 2980)

FossilOrigin-Name: 97491d4eb5fc24d8f5cc7605db844359ecc6a818
diff --git a/test/shared_err.test b/test/shared_err.test
new file mode 100644
index 0000000..16d629d
--- /dev/null
+++ b/test/shared_err.test
@@ -0,0 +1,137 @@
+# 2005 December 30
+#
+# 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.
+#
+#***********************************************************************
+#
+# The focus of the tests in this file are IO errors that occur in a shared
+# cache context. What happens to connection B if one connection A encounters
+# an IO-error whilst reading or writing the file-system?
+#
+# $Id: shared_err.test,v 1.1 2006/01/20 10:55:05 danielk1977 Exp $
+
+proc skip {args} {}
+
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+db close
+
+ifcapable !shared_cache||!subquery {
+  finish_test
+  return
+}
+set ::enable_shared_cache [sqlite3_enable_shared_cache 1]
+
+skip \
+do_ioerr_test shared_ioerr-1 -tclprep {
+  sqlite3 db2 test.db
+  execsql {
+    PRAGMA read_uncommitted = 1;
+    CREATE TABLE t1(a,b,c);
+    BEGIN;
+    SELECT * FROM sqlite_master;
+  } db2
+} -sqlbody {
+  SELECT * FROM sqlite_master;
+  INSERT INTO t1 VALUES(1,2,3);
+  BEGIN TRANSACTION;
+  INSERT INTO t1 VALUES(1,2,3);
+  INSERT INTO t1 VALUES(4,5,6);
+  ROLLBACK;
+  SELECT * FROM t1;
+  BEGIN TRANSACTION;
+  INSERT INTO t1 VALUES(1,2,3);
+  INSERT INTO t1 VALUES(4,5,6);
+  COMMIT;
+  SELECT * FROM t1;
+  DELETE FROM t1 WHERE a<100;
+} -cleanup {
+  do_test shared_ioerr-$n.cleanup.1 {
+    set res [catchsql {
+      SELECT * FROM t1;
+    } db2]
+    set possible_results [list            \
+      "1 {disk I/O error}"                \
+      "0 {1 2 3}"                         \
+      "0 {1 2 3 1 2 3 4 5 6}"             \
+      "0 {1 2 3 1 2 3 4 5 6 1 2 3 4 5 6}" \
+      "0 {}"                              \
+    ]
+    set rc [expr [lsearch -exact $possible_results $res] >= 0]
+    if {$rc != 1} {
+      puts ""
+      puts "Result: $res"
+    }
+    set rc
+  } {1}
+  db2 close
+}
+
+do_ioerr_test shared_ioerr-2 -tclprep {
+  sqlite3 db2 test.db
+  execsql {
+    PRAGMA read_uncommitted = 1;
+    BEGIN;
+    CREATE TABLE t1(a, b);
+    INSERT INTO t1(oid) VALUES(NULL);
+    INSERT INTO t1(oid) SELECT NULL FROM t1;
+    INSERT INTO t1(oid) SELECT NULL FROM t1;
+    INSERT INTO t1(oid) SELECT NULL FROM t1;
+    INSERT INTO t1(oid) SELECT NULL FROM t1;
+    INSERT INTO t1(oid) SELECT NULL FROM t1;
+    INSERT INTO t1(oid) SELECT NULL FROM t1;
+    INSERT INTO t1(oid) SELECT NULL FROM t1;
+    INSERT INTO t1(oid) SELECT NULL FROM t1;
+    INSERT INTO t1(oid) SELECT NULL FROM t1;
+    INSERT INTO t1(oid) SELECT NULL FROM t1;
+    UPDATE t1 set a = oid, b = 'abcdefghijklmnopqrstuvwxyz0123456789';
+    CREATE INDEX i1 ON t1(a);
+    COMMIT;
+    BEGIN;
+    SELECT * FROM sqlite_master;
+  } db2
+} -tclbody {
+  set ::residx 0
+  execsql {DELETE FROM t1 WHERE 0 = (a % 2);}
+  incr ::residx
+
+  # When this transaction begins the table contains 512 entries. The
+  # two statements together add 512+146 more if it succeeds. 
+  # (1024/7==146)
+  execsql {BEGIN;}
+  execsql {INSERT INTO t1 SELECT a+1, b FROM t1;}
+  execsql {INSERT INTO t1 SELECT 'string' || a, b FROM t1 WHERE 0 = (a%7);}
+  execsql {COMMIT;}
+
+  incr ::residx
+} -cleanup {
+  do_test shared_ioerr-2.$n.cleanup.1 {
+    set res [catchsql {
+      SELECT max(a), min(a), count(*) FROM (SELECT a FROM t1 order by a);
+    } db2]
+    set possible_results [list \
+      {0 {1024 1 1024}}        \
+      {0 {1023 1 512}}         \
+      {0 {string994 1 1170}}   \
+    ]
+    set idx [lsearch -exact $possible_results $res]
+    set success [expr {$idx==$::residx || $res=="1 {disk I/O error}"}]
+    if {!$success} {
+      puts ""
+      puts "Result: \"$res\" ($::residx)"
+    }
+    set success
+  } {1}
+  db2 close
+}
+
+catch {db close}
+sqlite3_enable_shared_cache $::enable_shared_cache
+finish_test
+