SQLite

Artifact [f72c9fbe18]
Login

Artifact f72c9fbe1802500f0d97e768f947ae5c703c0152:


# 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.2 2006/01/20 16:32: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]

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-1.$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
}

# This test is designed to provoke an IO error when a cursor position is
# "saved" (because another cursor is going to modify the underlying table). 
# 
do_ioerr_test shared_ioerr-3 -tclprep {
  sqlite3 db2 test.db
  execsql {
    PRAGMA read_uncommitted = 1;
    BEGIN;
    CREATE TABLE t1(a, b, UNIQUE(a, b));
  } db2
  for {set i 0} {$i < 5} {incr i} {
    set a [string repeat $i 10]
    set b [string repeat $i 2000]
    execsql {INSERT INTO t1 VALUES($a, $b)} db2
  }
  execsql {COMMIT} db2
  set ::DB2 [sqlite3_connection_pointer db2]
  set ::STMT [sqlite3_prepare $::DB2 "SELECT a FROM t1 ORDER BY a" -1 DUMMY]
  sqlite3_step $::STMT       ;# Cursor points at 0000000000
  sqlite3_step $::STMT       ;# Cursor points at 1111111111
} -tclbody {
  execsql {
    INSERT INTO t1 VALUES(6, NULL);
  }
} -cleanup {
  do_test shared_ioerr-3.$n.cleanup.1 {
    sqlite3_step $::STMT
  } {SQLITE_ROW}
  do_test shared_ioerr-3.$n.cleanup.2 {
    sqlite3_column_text $::STMT 0
  } {2222222222}
  do_test shared_ioerr-3.$n.cleanup.3 {
    sqlite3_finalize $::STMT
  } {SQLITE_OK}
# db2 eval {select * from sqlite_master}
  db2 close
}

catch {db close}
sqlite3_enable_shared_cache $::enable_shared_cache
finish_test