SQLite

Check-in [65ff754e35]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Fix the sharedA.test module so that it does not attempt to run TCL callbacks on a different thread from where the interpreter was originally created.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | shared-cache-fixes
Files: files | file ages | folders
SHA1: 65ff754e3521aa8ee9135d235166cac2a8f57ebd
User & Date: drh 2013-05-15 16:08:33.601
Context
2013-05-15
17:08
Make sure an sqlite3_close() or a rollback on one shared-cache connection does not disrupt the operation of other connections using the same shared cache. Fix for ticket [e636a050b709]. (check-in: 5cc1cc55d2 user: drh tags: trunk)
16:08
Fix the sharedA.test module so that it does not attempt to run TCL callbacks on a different thread from where the interpreter was originally created. (Closed-Leaf check-in: 65ff754e35 user: drh tags: shared-cache-fixes)
15:53
Do not run sharedA.test if the system is not threadsafe. (check-in: d484eaf8d6 user: dan tags: shared-cache-fixes)
Changes
Unified Diff Ignore Whitespace Patch
Changes to test/sharedA.test.
85
86
87
88
89
90
91
















92
93
94
95

96





97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114




115
116
117
118
119
120
121
122
123
124
125
126
127












128
129
130


131
132
133
134
135
136
137
138
139
140



141
142
143
144
145
146
147
148
149
150
151
152

do_test 1.3 {
  db2 close
} {}

#-------------------------------------------------------------------------
#
















testvfs tvfs
tvfs filter xRead
tvfs script read_callback
proc read_callback {args} { }







do_test 2.1 {
  forcedelete test.db test.db2
  sqlite3 db1 test.db -vfs tvfs
  db1 eval { ATTACH 'test.db2' AS two }

  db1 eval {
    CREATE TABLE t1(x);
    INSERT INTO t1 VALUES(1);
    INSERT INTO t1 VALUES(2);
    INSERT INTO t1 VALUES(3);
    CREATE TABLE two.t2(x);
    INSERT INTO t2 SELECT * FROM t1;
  }

  sqlite3 db2 test.db -vfs tvfs
  db2 eval { SELECT * FROM t1 }
} {1 2 3}





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);
      INSERT INTO t2 VALUES('value!');
  }
} {}













set ::bFired 0
proc read_callback {call file args} { 
  if { $::bFired==0 && [string match *test.db2-journal $file] } {


    sqlthread spawn ::thread_result [subst -nocommands {
      sqlite3_step $::STMT
      set rc [sqlite3_finalize $::STMT]
    }]
    after 1000 { set ::bFired 1 }
    vwait ::bFired
  }
}
do_test 2.4 { db1 eval ROLLBACK } {}




if {[info exists ::thread_result]==0} { vwait ::thread_result }
do_test 2.5 { 
  list $::thread_result [sqlite3_errmsg db2] 
} {SQLITE_SCHEMA {database schema has changed}}

db1 close
db2 close
tvfs delete

sqlite3_enable_shared_cache $::enable_shared_cache
finish_test








>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

|
<
<
>
|
>
>
>
>
>


















>
>
>
>


<
<
<
<







>
>
>
>
>
>
>
>
>
>
>
>
|

|
>
>




|
|


|

>
>
>

|









<
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109


110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140




141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188


do_test 1.3 {
  db2 close
} {}

#-------------------------------------------------------------------------
#
# 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



# 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
  db1 eval { ATTACH 'test.db2' AS two }

  db1 eval {
    CREATE TABLE t1(x);
    INSERT INTO t1 VALUES(1);
    INSERT INTO t1 VALUES(2);
    INSERT INTO t1 VALUES(3);
    CREATE TABLE two.t2(x);
    INSERT INTO t2 SELECT * FROM t1;
  }

  sqlite3 db2 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]




  db1 eval {
    BEGIN;
      CREATE INDEX i1 ON t1(x);
      INSERT INTO t2 VALUES('value!');
  }
} {}

# 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 {[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 ::some_time_later 1 }
    vwait ::some_time_later
  }
}
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.4 { 
  list $::thread_result [sqlite3_errmsg db2] 
} {SQLITE_SCHEMA {database schema has changed}}

db1 close
db2 close
tvfs delete

sqlite3_enable_shared_cache $::enable_shared_cache
finish_test