OLD | NEW |
| (Empty) |
1 # 2007 March 24 | |
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 # This file implements regression tests for SQLite library. | |
12 # | |
13 # $Id: exclusive2.test,v 1.10 2008/11/27 02:22:11 drh Exp $ | |
14 | |
15 set testdir [file dirname $argv0] | |
16 source $testdir/tester.tcl | |
17 | |
18 # Do not use a codec for tests in this file, as the database file is | |
19 # manipulated directly using tcl scripts (using the [hexio_write] command). | |
20 # | |
21 do_not_use_codec | |
22 | |
23 ifcapable {!pager_pragmas} { | |
24 finish_test | |
25 return | |
26 } | |
27 | |
28 # Tests in this file verify that locking_mode=exclusive causes SQLite to | |
29 # use cached pages even if the database is changed on disk. This doesn't | |
30 # work with mmap. | |
31 if {[permutation]=="mmap"} { | |
32 finish_test | |
33 return | |
34 } | |
35 | |
36 # This module does not work right if the cache spills at unexpected | |
37 # moments. So disable the soft-heap-limit. | |
38 # | |
39 sqlite3_soft_heap_limit 0 | |
40 | |
41 proc pagerChangeCounter {filename new {fd ""}} { | |
42 if {$fd==""} { | |
43 set fd [open $filename RDWR] | |
44 fconfigure $fd -translation binary -encoding binary | |
45 set needClose 1 | |
46 } else { | |
47 set needClose 0 | |
48 } | |
49 if {$new ne ""} { | |
50 seek $fd 24 | |
51 set a [expr {($new&0xFF000000)>>24}] | |
52 set b [expr {($new&0x00FF0000)>>16}] | |
53 set c [expr {($new&0x0000FF00)>>8}] | |
54 set d [expr {($new&0x000000FF)}] | |
55 puts -nonewline $fd [binary format cccc $a $b $c $d] | |
56 flush $fd | |
57 } | |
58 | |
59 seek $fd 24 | |
60 foreach {a b c d} [list 0 0 0 0] {} | |
61 binary scan [read $fd 4] cccc a b c d | |
62 set ret [expr ($a&0x000000FF)<<24] | |
63 incr ret [expr ($b&0x000000FF)<<16] | |
64 incr ret [expr ($c&0x000000FF)<<8] | |
65 incr ret [expr ($d&0x000000FF)<<0] | |
66 | |
67 if {$needClose} {close $fd} | |
68 return $ret | |
69 } | |
70 | |
71 proc readPagerChangeCounter {filename} { | |
72 set fd [open $filename RDONLY] | |
73 fconfigure $fd -translation binary -encoding binary | |
74 | |
75 seek $fd 24 | |
76 foreach {a b c d} [list 0 0 0 0] {} | |
77 binary scan [read $fd 4] cccc a b c d | |
78 set ret [expr ($a&0x000000FF)<<24] | |
79 incr ret [expr ($b&0x000000FF)<<16] | |
80 incr ret [expr ($c&0x000000FF)<<8] | |
81 incr ret [expr ($d&0x000000FF)<<0] | |
82 | |
83 close $fd | |
84 return $ret | |
85 } | |
86 | |
87 | |
88 proc t1sig {{db db}} { | |
89 execsql {SELECT count(*), md5sum(a) FROM t1} $db | |
90 } | |
91 do_test exclusive2-1.0 { | |
92 readPagerChangeCounter test.db | |
93 } {0} | |
94 | |
95 #----------------------------------------------------------------------- | |
96 # The following tests - exclusive2-1.X - check that: | |
97 # | |
98 # 1-3: Build a database with connection 1, calculate a signature. | |
99 # 4-7: Modify the database using a second connection in a way that | |
100 # does not modify the freelist, then reset the pager change-counter | |
101 # to the value it had before the modifications. | |
102 # 8: Check that using the first connection, the database signature | |
103 # is still the same. This is because it uses the in-memory cache. | |
104 # It can't tell the db has changed because we reset the change-counter. | |
105 # 9: Increment the change-counter. | |
106 # 10: Ensure that the first connection now sees the updated database. It | |
107 # sees the change-counter has been incremented and discards the | |
108 # invalid in-memory cache. | |
109 # | |
110 # This will only work if the database cache is large enough to hold | |
111 # the entire database. In the case of 1024 byte pages, this means | |
112 # the cache size must be at least 17. Otherwise, some pages will be | |
113 # loaded from the database file in step 8. | |
114 # | |
115 # For similar reasons, this test does not work with the memsubsys1 permutation. | |
116 # Permutation memsubsys1 configures the pcache subsystem to use a static | |
117 # allocation of 24 pages (shared between all pagers). This is not enough for | |
118 # this test. | |
119 # | |
120 do_test exclusive2-1.1 { | |
121 execsql { | |
122 BEGIN; | |
123 CREATE TABLE t1(a, b); | |
124 INSERT INTO t1(a) VALUES(randstr(10, 400)); | |
125 INSERT INTO t1(a) VALUES(randstr(10, 400)); | |
126 INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; | |
127 INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; | |
128 INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; | |
129 INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; | |
130 INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; | |
131 COMMIT; | |
132 SELECT count(*) FROM t1; | |
133 } | |
134 } {64} | |
135 do_test exclusive2-1.2.1 { | |
136 # Make sure the pager cache is large enough to store the | |
137 # entire database. | |
138 set nPage [expr [file size test.db]/1024] | |
139 if {$::SQLITE_DEFAULT_CACHE_SIZE < $nPage} { | |
140 execsql "PRAGMA cache_size = $nPage" | |
141 } | |
142 expr {[execsql {PRAGMA cache_size}] >= $nPage} | |
143 } {1} | |
144 do_test exclusive2-1.2 { | |
145 set ::sig [t1sig] | |
146 readPagerChangeCounter test.db | |
147 } {1} | |
148 do_test exclusive2-1.3 { | |
149 t1sig | |
150 } $::sig | |
151 do_test exclusive2-1.4 { | |
152 sqlite3 db2 test.db | |
153 t1sig db2 | |
154 } $::sig | |
155 do_test exclusive2-1.5 { | |
156 execsql { | |
157 UPDATE t1 SET b=a, a=NULL; | |
158 } db2 | |
159 expr {[t1sig db2] eq $::sig} | |
160 } 0 | |
161 do_test exclusive2-1.6 { | |
162 readPagerChangeCounter test.db | |
163 } {2} | |
164 do_test exclusive2-1.7 { | |
165 pagerChangeCounter test.db 1 | |
166 } {1} | |
167 if {[permutation] != "memsubsys1"} { | |
168 do_test exclusive2-1.9 { | |
169 t1sig | |
170 expr {[t1sig] eq $::sig} | |
171 } {1} | |
172 } | |
173 do_test exclusive2-1.10 { | |
174 pagerChangeCounter test.db 2 | |
175 } {2} | |
176 do_test exclusive2-1.11 { | |
177 expr {[t1sig] eq $::sig} | |
178 } {0} | |
179 db2 close | |
180 | |
181 #-------------------------------------------------------------------- | |
182 # These tests - exclusive2-2.X - are similar to exclusive2-1.X, | |
183 # except that they are run with locking_mode=EXCLUSIVE. | |
184 # | |
185 # 1-3: Build a database with exclusive-access connection 1, | |
186 # calculate a signature. | |
187 # 4: Corrupt the database by writing 10000 bytes of garbage | |
188 # starting at the beginning of page 2. Check that connection 1 | |
189 # still works. It should be accessing the in-memory cache. | |
190 # 5-6: Modify the dataase change-counter. Connection 1 still works | |
191 # entirely from in-memory cache, because it doesn't check the | |
192 # change-counter. | |
193 # 7-8 Set the locking-mode back to normal. After the db is unlocked, | |
194 # SQLite detects the modified change-counter and discards the | |
195 # in-memory cache. Then it finds the corruption caused in step 4.... | |
196 # | |
197 # As above, this test is only applicable if the pager cache is | |
198 # large enough to hold the entire database. With 1024 byte pages, | |
199 # this means 19 pages. We also need to disable the soft-heap-limit | |
200 # to prevent memory-induced cache spills. | |
201 # | |
202 do_test exclusive2-2.1 { | |
203 execsql {PRAGMA cache_size=1000;} | |
204 execsql {PRAGMA locking_mode = exclusive;} | |
205 execsql { | |
206 BEGIN; | |
207 DELETE FROM t1; | |
208 INSERT INTO t1(a) VALUES(randstr(10, 400)); | |
209 INSERT INTO t1(a) VALUES(randstr(10, 400)); | |
210 INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; | |
211 INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; | |
212 INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; | |
213 INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; | |
214 INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; | |
215 COMMIT; | |
216 SELECT count(*) FROM t1; | |
217 } | |
218 } {64} | |
219 do_test exclusive2-2.2.1 { | |
220 # Make sure the pager cache is large enough to store the | |
221 # entire database. | |
222 set nPage [expr [file size test.db]/1024] | |
223 if {$::SQLITE_DEFAULT_CACHE_SIZE < $nPage} { | |
224 execsql "PRAGMA cache_size = $nPage" | |
225 } | |
226 expr {[execsql {PRAGMA cache_size}] >= $nPage} | |
227 } {1} | |
228 do_test exclusive2-2.2 { | |
229 set ::sig [t1sig] | |
230 readPagerChangeCounter test.db | |
231 } {3} | |
232 do_test exclusive2-2.3 { | |
233 t1sig | |
234 } $::sig | |
235 | |
236 do_test exclusive2-2.4 { | |
237 set ::fd [open test.db RDWR] | |
238 fconfigure $::fd -translation binary | |
239 seek $::fd 1024 | |
240 puts -nonewline $::fd [string repeat [binary format c 0] 10000] | |
241 flush $::fd | |
242 t1sig | |
243 } $::sig | |
244 | |
245 do_test exclusive2-2.5 { | |
246 pagerChangeCounter test.db 5 $::fd | |
247 } {5} | |
248 do_test exclusive2-2.6 { | |
249 t1sig | |
250 } $::sig | |
251 do_test exclusive2-2.7 { | |
252 execsql {PRAGMA locking_mode = normal} | |
253 t1sig | |
254 } $::sig | |
255 | |
256 do_test exclusive2-2.8 { | |
257 set rc [catch {t1sig} msg] | |
258 list $rc $msg | |
259 } {1 {database disk image is malformed}} | |
260 | |
261 #-------------------------------------------------------------------- | |
262 # These tests - exclusive2-3.X - verify that the pager change-counter | |
263 # is only incremented by the first change when in exclusive access | |
264 # mode. In normal mode, the change-counter is incremented once | |
265 # per write-transaction. | |
266 # | |
267 | |
268 db close | |
269 catch {close $::fd} | |
270 forcedelete test.db | |
271 forcedelete test.db-journal | |
272 | |
273 do_test exclusive2-3.0 { | |
274 sqlite3 db test.db | |
275 execsql { | |
276 BEGIN; | |
277 CREATE TABLE t1(a UNIQUE); | |
278 INSERT INTO t1 VALUES(randstr(200, 200)); | |
279 INSERT INTO t1 VALUES(randstr(200, 200)); | |
280 COMMIT; | |
281 } | |
282 readPagerChangeCounter test.db | |
283 } {1} | |
284 do_test exclusive2-3.1 { | |
285 execsql { | |
286 INSERT INTO t1 VALUES(randstr(200, 200)); | |
287 } | |
288 readPagerChangeCounter test.db | |
289 } {2} | |
290 do_test exclusive2-3.2 { | |
291 execsql { | |
292 INSERT INTO t1 VALUES(randstr(200, 200)); | |
293 } | |
294 readPagerChangeCounter test.db | |
295 } {3} | |
296 do_test exclusive2-3.3 { | |
297 execsql { | |
298 PRAGMA locking_mode = exclusive; | |
299 INSERT INTO t1 VALUES(randstr(200, 200)); | |
300 } | |
301 readPagerChangeCounter test.db | |
302 } {4} | |
303 do_test exclusive2-3.4 { | |
304 execsql { | |
305 INSERT INTO t1 VALUES(randstr(200, 200)); | |
306 } | |
307 readPagerChangeCounter test.db | |
308 } {4} | |
309 do_test exclusive2-3.5 { | |
310 execsql { | |
311 PRAGMA locking_mode = normal; | |
312 INSERT INTO t1 VALUES(randstr(200, 200)); | |
313 } | |
314 readPagerChangeCounter test.db | |
315 } {4} | |
316 do_test exclusive2-3.6 { | |
317 execsql { | |
318 INSERT INTO t1 VALUES(randstr(200, 200)); | |
319 } | |
320 readPagerChangeCounter test.db | |
321 } {5} | |
322 sqlite3_soft_heap_limit $cmdlinearg(soft-heap-limit) | |
323 | |
324 finish_test | |
OLD | NEW |