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