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