modernc.org/cc@v1.0.1/v2/testdata/_sqlite/test/sharedA.test (about)

     1  # 2013 May 14
     2  #
     3  # The author disclaims copyright to this source code.  In place of
     4  # a legal notice, here is a blessing:
     5  #
     6  #    May you do good and not evil.
     7  #    May you find forgiveness for yourself and forgive others.
     8  #    May you share freely, never taking more than you give.
     9  #
    10  #***********************************************************************
    11  #
    12  # Test some specific circumstances to do with shared cache mode.
    13  #
    14  
    15  
    16  set testdir [file dirname $argv0]
    17  source $testdir/tester.tcl
    18  if {[run_thread_tests]==0} { finish_test ; return }
    19  db close
    20  set ::testprefix sharedA
    21  
    22  set ::enable_shared_cache [sqlite3_enable_shared_cache 1]
    23  
    24  #-------------------------------------------------------------------------
    25  #
    26  do_test 0.1 {
    27    sqlite3 db1 test.db
    28    sqlite3 db2 test.db
    29  
    30    db1 eval {
    31      CREATE TABLE t1(x);
    32      INSERT INTO t1 VALUES(randomblob(100));
    33      INSERT INTO t1 SELECT randomblob(100) FROM t1;
    34      INSERT INTO t1 SELECT randomblob(100) FROM t1;
    35      INSERT INTO t1 SELECT randomblob(100) FROM t1;
    36      INSERT INTO t1 SELECT randomblob(100) FROM t1;
    37      INSERT INTO t1 SELECT randomblob(100) FROM t1;
    38      INSERT INTO t1 SELECT randomblob(100) FROM t1;
    39      CREATE INDEX i1 ON t1(x);
    40    }
    41  
    42    db1 eval {
    43      BEGIN;
    44      DROP INDEX i1;
    45    }
    46  
    47    db2 close
    48  
    49    db1 eval {
    50      INSERT INTO t1 SELECT randomblob(100) FROM t1;
    51      ROLLBACK;
    52      PRAGMA integrity_check;
    53    }
    54  } {ok}
    55  
    56  db1 close
    57  forcedelete test.db
    58  
    59  
    60  #-------------------------------------------------------------------------
    61  #
    62  do_test 1.1 {
    63    sqlite3 db1 test.db
    64    sqlite3 db2 test.db
    65    db2 eval {
    66      CREATE TABLE t1(x);
    67      INSERT INTO t1 VALUES(123);
    68    }
    69    db1 eval { 
    70      SELECT * FROM t1;
    71      CREATE INDEX i1 ON t1(x);
    72    }
    73  } {123}
    74  
    75  do_test 1.2 {
    76    db2 eval { SELECT * FROM t1 ORDER BY x; }
    77  
    78    db1 eval {
    79      BEGIN; DROP INDEX i1;
    80    }
    81    db1 close
    82  
    83    db2 eval { SELECT * FROM t1 ORDER BY x; }
    84  } {123}
    85  
    86  do_test 1.3 {
    87    db2 close
    88  } {}
    89  
    90  #-------------------------------------------------------------------------
    91  #
    92  # sqlite3RollbackAll() loops through all attached b-trees and rolls
    93  # back each one separately.  Then if the SQLITE_InternChanges flag is
    94  # set, it resets the schema.  Both of the above steps must be done
    95  # while holding a mutex, otherwise another thread might slip in and
    96  # try to use the new schema with the old data.
    97  #
    98  # The following sequence of tests attempt to verify that the actions
    99  # taken by sqlite3RollbackAll() are thread-atomic (that they cannot be
   100  # interrupted by a separate thread.)  
   101  #
   102  # Note that a TCL interpreter can only be used within the thread in which
   103  # it was originally created (because it uses thread-local-storage).  
   104  # The tvfs callbacks must therefore only run on the main thread.  
   105  # There is some trickery in the read_callback procedure to ensure that
   106  # this is the case.
   107  #
   108  testvfs tvfs
   109  
   110  # Set up two databases and two database connections.
   111  #
   112  #   db1:  main(test.db), two(test2.db)
   113  #   db2:  main(test.db)
   114  #
   115  # The cache for test.db is shared between db1 and db2.
   116  #
   117  do_test 2.1 {
   118    forcedelete test.db test.db2
   119    sqlite3 db1 test.db -vfs tvfs
   120    db1 eval { ATTACH 'test.db2' AS two }
   121  
   122    db1 eval {
   123      CREATE TABLE t1(x);
   124      INSERT INTO t1 VALUES(1);
   125      INSERT INTO t1 VALUES(2);
   126      INSERT INTO t1 VALUES(3);
   127      CREATE TABLE two.t2(x);
   128      INSERT INTO t2 SELECT * FROM t1;
   129    }
   130  
   131    sqlite3 db2 test.db -vfs tvfs
   132    db2 eval { SELECT * FROM t1 }
   133  } {1 2 3}
   134  
   135  # Create a prepared statement on db2 that will attempt a schema change
   136  # in test.db.  Meanwhile, start a transaction on db1 that changes
   137  # the schema of test.db and that creates a rollback journal on test2.db
   138  #
   139  do_test 2.2 {
   140    set ::STMT [sqlite3_prepare db2 "CREATE INDEX i1 ON t1(x)" -1 tail]
   141    db1 eval {
   142      BEGIN;
   143        CREATE INDEX i1 ON t1(x);
   144        INSERT INTO t2 VALUES('value!');
   145    }
   146  } {}
   147  
   148  # Set up a callback that will cause db2 to try to execute its
   149  # schema change when db1 accesses the journal file of test2.db.
   150  #
   151  # This callback will be invoked after the content of test.db has
   152  # be rolled back but before the schema has been reset.  If the
   153  # sqlite3RollbackAll() operation is not thread-atomic, then the
   154  # db2 statement in the callback will see old content with the newer
   155  # schema, which is wrong.
   156  #
   157  tvfs filter xRead
   158  tvfs script read_callback
   159  unset -nocomplain ::some_time_laster
   160  unset -nocomplain ::thread_result
   161  proc read_callback {call file args} { 
   162    if {[string match *test.db2-journal $file]} {
   163      tvfs filter {}   ;# Ensure that tvfs callbacks to do run on the
   164                        # child thread
   165      sqlthread spawn ::thread_result [subst -nocommands {
   166        sqlite3_step $::STMT
   167        set rc [sqlite3_finalize $::STMT]
   168      }]
   169      after 1000 { set ::some_time_later 1 }
   170      vwait ::some_time_later
   171    }
   172  }
   173  do_test 2.3 { db1 eval ROLLBACK } {}
   174  
   175  # Verify that the db2 statement invoked by the callback detected the
   176  # schema change.
   177  #
   178  if {[info exists ::thread_result]==0} { vwait ::thread_result }
   179  do_test 2.4 { 
   180    list $::thread_result [sqlite3_errmsg db2] 
   181  } {SQLITE_SCHEMA {database schema has changed}}
   182  
   183  db1 close
   184  db2 close
   185  tvfs delete
   186  
   187  sqlite3_enable_shared_cache $::enable_shared_cache
   188  finish_test