1cd7b91a7Sdan# 2013 May 14 2cd7b91a7Sdan# 3cd7b91a7Sdan# The author disclaims copyright to this source code. In place of 4cd7b91a7Sdan# a legal notice, here is a blessing: 5cd7b91a7Sdan# 6cd7b91a7Sdan# May you do good and not evil. 7cd7b91a7Sdan# May you find forgiveness for yourself and forgive others. 8cd7b91a7Sdan# May you share freely, never taking more than you give. 9cd7b91a7Sdan# 10cd7b91a7Sdan#*********************************************************************** 11cd7b91a7Sdan# 12cd7b91a7Sdan# Test some specific circumstances to do with shared cache mode. 13cd7b91a7Sdan# 14cd7b91a7Sdan 15cd7b91a7Sdan 16cd7b91a7Sdanset testdir [file dirname $argv0] 17cd7b91a7Sdansource $testdir/tester.tcl 18d5dd3305Sdanif {[run_thread_tests]==0} { finish_test ; return } 19cd7b91a7Sdandb close 20bf0e57a7Sdanset ::testprefix sharedA 21cd7b91a7Sdan 22*9c5e1e40Sdrhifcapable !shared_cache { 23*9c5e1e40Sdrh finish_test 24*9c5e1e40Sdrh return 25*9c5e1e40Sdrh} 26*9c5e1e40Sdrh 2769aedc8dSdanif {[atomic_batch_write test.db]} { 2869aedc8dSdan finish_test 2969aedc8dSdan return 3069aedc8dSdan} 3169aedc8dSdan 32cd7b91a7Sdanset ::enable_shared_cache [sqlite3_enable_shared_cache 1] 33cd7b91a7Sdan 34cd7b91a7Sdan#------------------------------------------------------------------------- 35cd7b91a7Sdan# 36bf0e57a7Sdando_test 0.1 { 37bf0e57a7Sdan sqlite3 db1 test.db 38bf0e57a7Sdan sqlite3 db2 test.db 39bf0e57a7Sdan 40bf0e57a7Sdan db1 eval { 41bf0e57a7Sdan CREATE TABLE t1(x); 42bf0e57a7Sdan INSERT INTO t1 VALUES(randomblob(100)); 43bf0e57a7Sdan INSERT INTO t1 SELECT randomblob(100) FROM t1; 44bf0e57a7Sdan INSERT INTO t1 SELECT randomblob(100) FROM t1; 45bf0e57a7Sdan INSERT INTO t1 SELECT randomblob(100) FROM t1; 46bf0e57a7Sdan INSERT INTO t1 SELECT randomblob(100) FROM t1; 47bf0e57a7Sdan INSERT INTO t1 SELECT randomblob(100) FROM t1; 48bf0e57a7Sdan INSERT INTO t1 SELECT randomblob(100) FROM t1; 49bf0e57a7Sdan CREATE INDEX i1 ON t1(x); 50bf0e57a7Sdan } 51bf0e57a7Sdan 52bf0e57a7Sdan db1 eval { 53bf0e57a7Sdan BEGIN; 54bf0e57a7Sdan DROP INDEX i1; 55bf0e57a7Sdan } 56bf0e57a7Sdan 57bf0e57a7Sdan db2 close 58bf0e57a7Sdan 59bf0e57a7Sdan db1 eval { 60bf0e57a7Sdan INSERT INTO t1 SELECT randomblob(100) FROM t1; 61bf0e57a7Sdan ROLLBACK; 62bf0e57a7Sdan PRAGMA integrity_check; 63bf0e57a7Sdan } 64bf0e57a7Sdan} {ok} 65bf0e57a7Sdan 66bf0e57a7Sdandb1 close 67bf0e57a7Sdanforcedelete test.db 68bf0e57a7Sdan 69bf0e57a7Sdan 70bf0e57a7Sdan#------------------------------------------------------------------------- 71bf0e57a7Sdan# 72cd7b91a7Sdando_test 1.1 { 73cd7b91a7Sdan sqlite3 db1 test.db 74cd7b91a7Sdan sqlite3 db2 test.db 75cd7b91a7Sdan db2 eval { 76cd7b91a7Sdan CREATE TABLE t1(x); 77cd7b91a7Sdan INSERT INTO t1 VALUES(123); 78cd7b91a7Sdan } 79cd7b91a7Sdan db1 eval { 80cd7b91a7Sdan SELECT * FROM t1; 81cd7b91a7Sdan CREATE INDEX i1 ON t1(x); 82cd7b91a7Sdan } 83cd7b91a7Sdan} {123} 84cd7b91a7Sdan 85cd7b91a7Sdando_test 1.2 { 86cd7b91a7Sdan db2 eval { SELECT * FROM t1 ORDER BY x; } 87cd7b91a7Sdan 88cd7b91a7Sdan db1 eval { 89cd7b91a7Sdan BEGIN; DROP INDEX i1; 90cd7b91a7Sdan } 91cd7b91a7Sdan db1 close 92cd7b91a7Sdan 93cd7b91a7Sdan db2 eval { SELECT * FROM t1 ORDER BY x; } 94cd7b91a7Sdan} {123} 95cd7b91a7Sdan 96cd7b91a7Sdando_test 1.3 { 97cd7b91a7Sdan db2 close 98cd7b91a7Sdan} {} 99cd7b91a7Sdan 100cd7b91a7Sdan#------------------------------------------------------------------------- 101cd7b91a7Sdan# 1022ab2d55fSdrh# sqlite3RollbackAll() loops through all attached b-trees and rolls 1032ab2d55fSdrh# back each one separately. Then if the SQLITE_InternChanges flag is 1042ab2d55fSdrh# set, it resets the schema. Both of the above steps must be done 1052ab2d55fSdrh# while holding a mutex, otherwise another thread might slip in and 1062ab2d55fSdrh# try to use the new schema with the old data. 1072ab2d55fSdrh# 1082ab2d55fSdrh# The following sequence of tests attempt to verify that the actions 1092ab2d55fSdrh# taken by sqlite3RollbackAll() are thread-atomic (that they cannot be 1102ab2d55fSdrh# interrupted by a separate thread.) 1112ab2d55fSdrh# 1122ab2d55fSdrh# Note that a TCL interpreter can only be used within the thread in which 1132ab2d55fSdrh# it was originally created (because it uses thread-local-storage). 1142ab2d55fSdrh# The tvfs callbacks must therefore only run on the main thread. 1152ab2d55fSdrh# There is some trickery in the read_callback procedure to ensure that 1162ab2d55fSdrh# this is the case. 1172ab2d55fSdrh# 118cd7b91a7Sdantestvfs tvfs 119cd7b91a7Sdan 1202ab2d55fSdrh# Set up two databases and two database connections. 1212ab2d55fSdrh# 1222ab2d55fSdrh# db1: main(test.db), two(test2.db) 1232ab2d55fSdrh# db2: main(test.db) 1242ab2d55fSdrh# 1252ab2d55fSdrh# The cache for test.db is shared between db1 and db2. 1262ab2d55fSdrh# 127cd7b91a7Sdando_test 2.1 { 128cd7b91a7Sdan forcedelete test.db test.db2 129cd7b91a7Sdan sqlite3 db1 test.db -vfs tvfs 130cd7b91a7Sdan db1 eval { ATTACH 'test.db2' AS two } 131cd7b91a7Sdan 132cd7b91a7Sdan db1 eval { 133cd7b91a7Sdan CREATE TABLE t1(x); 134cd7b91a7Sdan INSERT INTO t1 VALUES(1); 135cd7b91a7Sdan INSERT INTO t1 VALUES(2); 136cd7b91a7Sdan INSERT INTO t1 VALUES(3); 137cd7b91a7Sdan CREATE TABLE two.t2(x); 138cd7b91a7Sdan INSERT INTO t2 SELECT * FROM t1; 139cd7b91a7Sdan } 140cd7b91a7Sdan 141cd7b91a7Sdan sqlite3 db2 test.db -vfs tvfs 142cd7b91a7Sdan db2 eval { SELECT * FROM t1 } 143cd7b91a7Sdan} {1 2 3} 144cd7b91a7Sdan 1452ab2d55fSdrh# Create a prepared statement on db2 that will attempt a schema change 1462ab2d55fSdrh# in test.db. Meanwhile, start a transaction on db1 that changes 1472ab2d55fSdrh# the schema of test.db and that creates a rollback journal on test2.db 1482ab2d55fSdrh# 149cd7b91a7Sdando_test 2.2 { 150cd7b91a7Sdan set ::STMT [sqlite3_prepare db2 "CREATE INDEX i1 ON t1(x)" -1 tail] 151cd7b91a7Sdan db1 eval { 152cd7b91a7Sdan BEGIN; 153cd7b91a7Sdan CREATE INDEX i1 ON t1(x); 154cd7b91a7Sdan INSERT INTO t2 VALUES('value!'); 155cd7b91a7Sdan } 156cd7b91a7Sdan} {} 157cd7b91a7Sdan 1582ab2d55fSdrh# Set up a callback that will cause db2 to try to execute its 1592ab2d55fSdrh# schema change when db1 accesses the journal file of test2.db. 1602ab2d55fSdrh# 1612ab2d55fSdrh# This callback will be invoked after the content of test.db has 1622ab2d55fSdrh# be rolled back but before the schema has been reset. If the 1632ab2d55fSdrh# sqlite3RollbackAll() operation is not thread-atomic, then the 1642ab2d55fSdrh# db2 statement in the callback will see old content with the newer 1652ab2d55fSdrh# schema, which is wrong. 1662ab2d55fSdrh# 1672ab2d55fSdrhtvfs filter xRead 1682ab2d55fSdrhtvfs script read_callback 1692ab2d55fSdrhunset -nocomplain ::some_time_laster 1702ab2d55fSdrhunset -nocomplain ::thread_result 171cd7b91a7Sdanproc read_callback {call file args} { 1722ab2d55fSdrh if {[string match *test.db2-journal $file]} { 1732ab2d55fSdrh tvfs filter {} ;# Ensure that tvfs callbacks to do run on the 1742ab2d55fSdrh # child thread 175cd7b91a7Sdan sqlthread spawn ::thread_result [subst -nocommands { 176cd7b91a7Sdan sqlite3_step $::STMT 177cd7b91a7Sdan set rc [sqlite3_finalize $::STMT] 178cd7b91a7Sdan }] 1792ab2d55fSdrh after 1000 { set ::some_time_later 1 } 1802ab2d55fSdrh vwait ::some_time_later 181cd7b91a7Sdan } 182cd7b91a7Sdan} 1832ab2d55fSdrhdo_test 2.3 { db1 eval ROLLBACK } {} 184cd7b91a7Sdan 1852ab2d55fSdrh# Verify that the db2 statement invoked by the callback detected the 1862ab2d55fSdrh# schema change. 1872ab2d55fSdrh# 188cd7b91a7Sdanif {[info exists ::thread_result]==0} { vwait ::thread_result } 1892ab2d55fSdrhdo_test 2.4 { 190cd7b91a7Sdan list $::thread_result [sqlite3_errmsg db2] 191cd7b91a7Sdan} {SQLITE_SCHEMA {database schema has changed}} 192cd7b91a7Sdan 193cd7b91a7Sdandb1 close 194cd7b91a7Sdandb2 close 195cd7b91a7Sdantvfs delete 196cd7b91a7Sdan 197cd7b91a7Sdansqlite3_enable_shared_cache $::enable_shared_cache 198cd7b91a7Sdanfinish_test 199