-C Do\snot\srun\ssharedA.test\sif\sthe\ssystem\sis\snot\sthreadsafe.
-D 2013-05-15T15:53:52.221
+C Fix\sthe\ssharedA.test\smodule\sso\sthat\sit\sdoes\snot\sattempt\sto\srun\sTCL\scallbacks\non\sa\sdifferent\sthread\sfrom\swhere\sthe\sinterpreter\swas\soriginally\screated.
+D 2013-05-15T16:08:33.601
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in ce81671efd6223d19d4c8c6b88ac2c4134427111
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
F test/shared7.test 960760bc8d03e1419e70dea69cf41db62853616e
F test/shared8.test b27befbefbe7f4517f1d6b7ff8f64a41ec74165d
F test/shared9.test 5f2a8f79b4d6c7d107a01ffa1ed05ae7e6333e21
-F test/sharedA.test 8eddeda2407bdcd335ec8b8ba731aefc0e3e8232
+F test/sharedA.test 0cdf1a76dfa00e6beee66af5b534b1e8df2720f5
F test/shared_err.test 0079c05c97d88cfa03989b7c20a8b266983087aa
F test/sharedlock.test ffa0a3c4ac192145b310f1254f8afca4d553eabf
F test/shell1.test 4a2f57952719972c6f862134463f8712e953c038
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381
F tool/win/sqlite.vsix 97894c2790eda7b5bce3cc79cb2a8ec2fde9b3ac
-P 47dd65a890955f333d431e275f3f4d95d34a5ba5
-R 7b8246f3bd54a0ac6d93356e2396ef91
-U dan
-Z d923d1c201be4d3e0bbb63e5d84a5ca9
+P d484eaf8d6dfaf2c1065b93b2a52a6db91c09fa4
+R 2b3d38cfaeff54ce7cad59c19d675f6e
+U drh
+Z adae8d677b3886690161b439bdcd47db
#-------------------------------------------------------------------------
#
+# sqlite3RollbackAll() loops through all attached b-trees and rolls
+# back each one separately. Then if the SQLITE_InternChanges flag is
+# set, it resets the schema. Both of the above steps must be done
+# while holding a mutex, otherwise another thread might slip in and
+# try to use the new schema with the old data.
+#
+# The following sequence of tests attempt to verify that the actions
+# taken by sqlite3RollbackAll() are thread-atomic (that they cannot be
+# interrupted by a separate thread.)
+#
+# Note that a TCL interpreter can only be used within the thread in which
+# it was originally created (because it uses thread-local-storage).
+# The tvfs callbacks must therefore only run on the main thread.
+# There is some trickery in the read_callback procedure to ensure that
+# this is the case.
+#
testvfs tvfs
-tvfs filter xRead
-tvfs script read_callback
-proc read_callback {args} { }
+# Set up two databases and two database connections.
+#
+# db1: main(test.db), two(test2.db)
+# db2: main(test.db)
+#
+# The cache for test.db is shared between db1 and db2.
+#
do_test 2.1 {
forcedelete test.db test.db2
sqlite3 db1 test.db -vfs tvfs
db2 eval { SELECT * FROM t1 }
} {1 2 3}
+# Create a prepared statement on db2 that will attempt a schema change
+# in test.db. Meanwhile, start a transaction on db1 that changes
+# the schema of test.db and that creates a rollback journal on test2.db
+#
do_test 2.2 {
set ::STMT [sqlite3_prepare db2 "CREATE INDEX i1 ON t1(x)" -1 tail]
- set {} {}
-} {}
-
-do_test 2.3 {
db1 eval {
BEGIN;
CREATE INDEX i1 ON t1(x);
}
} {}
-set ::bFired 0
+# Set up a callback that will cause db2 to try to execute its
+# schema change when db1 accesses the journal file of test2.db.
+#
+# This callback will be invoked after the content of test.db has
+# be rolled back but before the schema has been reset. If the
+# sqlite3RollbackAll() operation is not thread-atomic, then the
+# db2 statement in the callback will see old content with the newer
+# schema, which is wrong.
+#
+tvfs filter xRead
+tvfs script read_callback
+unset -nocomplain ::some_time_laster
+unset -nocomplain ::thread_result
proc read_callback {call file args} {
- if { $::bFired==0 && [string match *test.db2-journal $file] } {
+ if {[string match *test.db2-journal $file]} {
+ tvfs filter {} ;# Ensure that tvfs callbacks to do run on the
+ # child thread
sqlthread spawn ::thread_result [subst -nocommands {
sqlite3_step $::STMT
set rc [sqlite3_finalize $::STMT]
}]
- after 1000 { set ::bFired 1 }
- vwait ::bFired
+ after 1000 { set ::some_time_later 1 }
+ vwait ::some_time_later
}
}
-do_test 2.4 { db1 eval ROLLBACK } {}
+do_test 2.3 { db1 eval ROLLBACK } {}
+# Verify that the db2 statement invoked by the callback detected the
+# schema change.
+#
if {[info exists ::thread_result]==0} { vwait ::thread_result }
-do_test 2.5 {
+do_test 2.4 {
list $::thread_result [sqlite3_errmsg db2]
} {SQLITE_SCHEMA {database schema has changed}}
sqlite3_enable_shared_cache $::enable_shared_cache
finish_test
-