wasCSharpSQLite – Blame information for rev 7
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | # 2010 April 13 |
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. The |
||
12 | # focus of this file is testing the operation of the library in |
||
13 | # "PRAGMA journal_mode=WAL" mode. |
||
14 | # |
||
15 | |||
16 | set testdir [file dirname $argv0] |
||
17 | source $testdir/tester.tcl |
||
18 | source $testdir/lock_common.tcl |
||
19 | source $testdir/wal_common.tcl |
||
20 | source $testdir/malloc_common.tcl |
||
21 | ifcapable !wal {finish_test ; return } |
||
22 | |||
23 | set a_string_counter 1 |
||
24 | proc a_string {n} { |
||
25 | global a_string_counter |
||
26 | incr a_string_counter |
||
27 | string range [string repeat "${a_string_counter}." $n] 1 $n |
||
28 | } |
||
29 | db func a_string a_string |
||
30 | |||
31 | #------------------------------------------------------------------------- |
||
32 | # When a rollback or savepoint rollback occurs, the client may remove |
||
33 | # elements from one of the hash tables in the wal-index. This block |
||
34 | # of test cases tests that nothing appears to go wrong when this is |
||
35 | # done. |
||
36 | # |
||
37 | do_test wal3-1.0 { |
||
38 | execsql { |
||
39 | PRAGMA cache_size = 2000; |
||
40 | PRAGMA page_size = 1024; |
||
41 | PRAGMA auto_vacuum = off; |
||
42 | PRAGMA synchronous = normal; |
||
43 | PRAGMA journal_mode = WAL; |
||
44 | PRAGMA wal_autocheckpoint = 0; |
||
45 | BEGIN; |
||
46 | CREATE TABLE t1(x); |
||
47 | INSERT INTO t1 VALUES( a_string(800) ); /* 1 */ |
||
48 | INSERT INTO t1 SELECT a_string(800) FROM t1; /* 2 */ |
||
49 | INSERT INTO t1 SELECT a_string(800) FROM t1; /* 4 */ |
||
50 | INSERT INTO t1 SELECT a_string(800) FROM t1; /* 8 */ |
||
51 | INSERT INTO t1 SELECT a_string(800) FROM t1; /* 16 */ |
||
52 | INSERT INTO t1 SELECT a_string(800) FROM t1; /* 32 */ |
||
53 | INSERT INTO t1 SELECT a_string(800) FROM t1; /* 64 */ |
||
54 | INSERT INTO t1 SELECT a_string(800) FROM t1; /* 128*/ |
||
55 | INSERT INTO t1 SELECT a_string(800) FROM t1; /* 256 */ |
||
56 | INSERT INTO t1 SELECT a_string(800) FROM t1; /* 512 */ |
||
57 | INSERT INTO t1 SELECT a_string(800) FROM t1; /* 1024 */ |
||
58 | INSERT INTO t1 SELECT a_string(800) FROM t1; /* 2048 */ |
||
59 | INSERT INTO t1 SELECT a_string(800) FROM t1 LIMIT 1970; /* 4018 */ |
||
60 | COMMIT; |
||
61 | PRAGMA cache_size = 10; |
||
62 | } |
||
63 | wal_frame_count test.db-wal 1024 |
||
64 | } 4056 |
||
65 | |||
66 | for {set i 1} {$i < 50} {incr i} { |
||
67 | |||
68 | do_test wal3-1.$i.1 { |
||
69 | set str [a_string 800] |
||
70 | execsql { UPDATE t1 SET x = $str WHERE rowid = $i } |
||
71 | lappend L [wal_frame_count test.db-wal 1024] |
||
72 | execsql { |
||
73 | BEGIN; |
||
74 | INSERT INTO t1 SELECT a_string(800) FROM t1 LIMIT 100; |
||
75 | ROLLBACK; |
||
76 | PRAGMA integrity_check; |
||
77 | } |
||
78 | } {ok} |
||
79 | |||
80 | # Check that everything looks OK from the point of view of an |
||
81 | # external connection. |
||
82 | # |
||
83 | sqlite3 db2 test.db |
||
84 | do_test wal3-1.$i.2 { |
||
85 | execsql { SELECT count(*) FROM t1 } db2 |
||
86 | } 4018 |
||
87 | do_test wal3-1.$i.3 { |
||
88 | execsql { SELECT x FROM t1 WHERE rowid = $i } |
||
89 | } $str |
||
90 | do_test wal3-1.$i.4 { |
||
91 | execsql { PRAGMA integrity_check } db2 |
||
92 | } {ok} |
||
93 | db2 close |
||
94 | |||
95 | # Check that the file-system in its current state can be recovered. |
||
96 | # |
||
97 | file copy -force test.db test2.db |
||
98 | file copy -force test.db-wal test2.db-wal |
||
99 | file delete -force test2.db-journal |
||
100 | sqlite3 db2 test2.db |
||
101 | do_test wal3-1.$i.5 { |
||
102 | execsql { SELECT count(*) FROM t1 } db2 |
||
103 | } 4018 |
||
104 | do_test wal3-1.$i.6 { |
||
105 | execsql { SELECT x FROM t1 WHERE rowid = $i } |
||
106 | } $str |
||
107 | do_test wal3-1.$i.7 { |
||
108 | execsql { PRAGMA integrity_check } db2 |
||
109 | } {ok} |
||
110 | db2 close |
||
111 | } |
||
112 | |||
113 | proc byte_is_zero {file offset} { |
||
114 | if {[file size test.db] <= $offset} { return 1 } |
||
115 | expr { [hexio_read $file $offset 1] == "00" } |
||
116 | } |
||
117 | |||
118 | do_multiclient_test i { |
||
119 | |||
120 | set testname(1) multiproc |
||
121 | set testname(2) singleproc |
||
122 | set tn $testname($i) |
||
123 | |||
124 | do_test wal3-2.$tn.1 { |
||
125 | sql1 { |
||
126 | PRAGMA page_size = 1024; |
||
127 | PRAGMA journal_mode = WAL; |
||
128 | } |
||
129 | sql1 { |
||
130 | CREATE TABLE t1(a, b); |
||
131 | INSERT INTO t1 VALUES(1, 'one'); |
||
132 | BEGIN; |
||
133 | SELECT * FROM t1; |
||
134 | } |
||
135 | } {1 one} |
||
136 | do_test wal3-2.$tn.2 { |
||
137 | sql2 { |
||
138 | CREATE TABLE t2(a, b); |
||
139 | INSERT INTO t2 VALUES(2, 'two'); |
||
140 | BEGIN; |
||
141 | SELECT * FROM t2; |
||
142 | } |
||
143 | } {2 two} |
||
144 | do_test wal3-2.$tn.3 { |
||
145 | sql3 { |
||
146 | CREATE TABLE t3(a, b); |
||
147 | INSERT INTO t3 VALUES(3, 'three'); |
||
148 | BEGIN; |
||
149 | SELECT * FROM t3; |
||
150 | } |
||
151 | } {3 three} |
||
152 | |||
153 | # Try to checkpoint the database using [db]. It should be possible to |
||
154 | # checkpoint everything except the table added by [db3] (checkpointing |
||
155 | # these frames would clobber the snapshot currently being used by [db2]). |
||
156 | # |
||
157 | # After [db2] has committed, a checkpoint can copy the entire log to the |
||
158 | # database file. Checkpointing after [db3] has committed is therefore a |
||
159 | # no-op, as the entire log has already been backfilled. |
||
160 | # |
||
161 | do_test wal3-2.$tn.4 { |
||
162 | sql1 { |
||
163 | COMMIT; |
||
164 | PRAGMA wal_checkpoint; |
||
165 | } |
||
166 | byte_is_zero test.db [expr $AUTOVACUUM ? 4*1024 : 3*1024] |
||
167 | } {1} |
||
168 | do_test wal3-2.$tn.5 { |
||
169 | sql2 { |
||
170 | COMMIT; |
||
171 | PRAGMA wal_checkpoint; |
||
172 | } |
||
173 | list [byte_is_zero test.db [expr $AUTOVACUUM ? 4*1024 : 3*1024]] \ |
||
174 | [byte_is_zero test.db [expr $AUTOVACUUM ? 5*1024 : 4*1024]] |
||
175 | } {0 1} |
||
176 | do_test wal3-2.$tn.6 { |
||
177 | sql3 { |
||
178 | COMMIT; |
||
179 | PRAGMA wal_checkpoint; |
||
180 | } |
||
181 | list [byte_is_zero test.db [expr $AUTOVACUUM ? 4*1024 : 3*1024]] \ |
||
182 | [byte_is_zero test.db [expr $AUTOVACUUM ? 5*1024 : 4*1024]] |
||
183 | } {0 1} |
||
184 | } |
||
185 | catch {db close} |
||
186 | |||
187 | #------------------------------------------------------------------------- |
||
188 | # Test that that for the simple test: |
||
189 | # |
||
190 | # CREATE TABLE x(y); |
||
191 | # INSERT INTO x VALUES('z'); |
||
192 | # PRAGMA wal_checkpoint; |
||
193 | # |
||
194 | # in WAL mode the xSync method is invoked as expected for each of |
||
195 | # synchronous=off, synchronous=normal and synchronous=full. |
||
196 | # |
||
197 | foreach {tn syncmode synccount} { |
||
198 | 1 off |
||
199 | {} |
||
200 | 2 normal |
||
201 | {test.db-wal normal test.db normal} |
||
202 | 3 full |
||
203 | {test.db-wal normal test.db-wal normal test.db-wal normal test.db normal} |
||
204 | } { |
||
205 | |||
206 | proc sync_counter {args} { |
||
207 | foreach {method filename id flags} $args break |
||
208 | lappend ::syncs [file tail $filename] $flags |
||
209 | } |
||
210 | do_test wal3-3.$tn { |
||
211 | file delete -force test.db test.db-wal test.db-journal |
||
212 | |||
213 | testvfs T |
||
214 | T filter {} |
||
215 | T script sync_counter |
||
216 | sqlite3 db test.db -vfs T |
||
217 | |||
218 | execsql "PRAGMA synchronous = $syncmode" |
||
219 | execsql { PRAGMA journal_mode = WAL } |
||
220 | |||
221 | set ::syncs [list] |
||
222 | T filter xSync |
||
223 | execsql { |
||
224 | CREATE TABLE x(y); |
||
225 | INSERT INTO x VALUES('z'); |
||
226 | PRAGMA wal_checkpoint; |
||
227 | } |
||
228 | T filter {} |
||
229 | set ::syncs |
||
230 | } $synccount |
||
231 | |||
232 | db close |
||
233 | T delete |
||
234 | } |
||
235 | |||
236 | #------------------------------------------------------------------------- |
||
237 | # When recovering the contents of a WAL file, a process obtains the WRITER |
||
238 | # lock, then locks all other bytes before commencing recovery. If it fails |
||
239 | # to lock all other bytes (because some other process is holding a read |
||
240 | # lock) it should retry up to 100 times. Then return SQLITE_PROTOCOL to the |
||
241 | # caller. Test this (test case wal3-4.3). |
||
242 | # |
||
243 | # Also test the effect of hitting an SQLITE_BUSY while attempting to obtain |
||
244 | # the WRITER lock (should be the same). Test case wal3-4.4. |
||
245 | # |
||
246 | proc lock_callback {method filename handle lock} { |
||
247 | lappend ::locks $lock |
||
248 | } |
||
249 | do_test wal3-4.1 { |
||
250 | testvfs T |
||
251 | T filter xShmLock |
||
252 | T script lock_callback |
||
253 | set ::locks [list] |
||
254 | sqlite3 db test.db -vfs T |
||
255 | execsql { SELECT * FROM x } |
||
256 | lrange $::locks 0 3 |
||
257 | } [list {0 1 lock exclusive} {1 7 lock exclusive} \ |
||
258 | {1 7 unlock exclusive} {0 1 unlock exclusive} \ |
||
259 | ] |
||
260 | do_test wal3-4.2 { |
||
261 | db close |
||
262 | set ::locks [list] |
||
263 | sqlite3 db test.db -vfs T |
||
264 | execsql { SELECT * FROM x } |
||
265 | lrange $::locks 0 3 |
||
266 | } [list {0 1 lock exclusive} {1 7 lock exclusive} \ |
||
267 | {1 7 unlock exclusive} {0 1 unlock exclusive} \ |
||
268 | ] |
||
269 | proc lock_callback {method filename handle lock} { |
||
270 | if {$lock == "1 7 lock exclusive"} { return SQLITE_BUSY } |
||
271 | return SQLITE_OK |
||
272 | } |
||
273 | puts " Warning: This next test case causes SQLite to call xSleep(1) 100 times." |
||
274 | puts " Normally this equates to a 100ms delay, but if SQLite is built on unix" |
||
275 | puts " without HAVE_USLEEP defined, it may be 100 seconds." |
||
276 | do_test wal3-4.3 { |
||
277 | db close |
||
278 | set ::locks [list] |
||
279 | sqlite3 db test.db -vfs T |
||
280 | catchsql { SELECT * FROM x } |
||
281 | } {1 {locking protocol}} |
||
282 | |||
283 | puts " Warning: Same again!" |
||
284 | proc lock_callback {method filename handle lock} { |
||
285 | if {$lock == "0 1 lock exclusive"} { return SQLITE_BUSY } |
||
286 | return SQLITE_OK |
||
287 | } |
||
288 | do_test wal3-4.4 { |
||
289 | db close |
||
290 | set ::locks [list] |
||
291 | sqlite3 db test.db -vfs T |
||
292 | catchsql { SELECT * FROM x } |
||
293 | } {1 {locking protocol}} |
||
294 | db close |
||
295 | T delete |
||
296 | |||
297 | |||
298 | #------------------------------------------------------------------------- |
||
299 | # Only one client may run recovery at a time. Test this mechanism. |
||
300 | # |
||
301 | # When client-2 tries to open a read transaction while client-1 is |
||
302 | # running recovery, it fails to obtain a lock on an aReadMark[] slot |
||
303 | # (because they are all locked by recovery). It then tries to obtain |
||
304 | # a shared lock on the RECOVER lock to see if there really is a |
||
305 | # recovery running or not. |
||
306 | # |
||
307 | # This block of tests checks the effect of an SQLITE_BUSY or SQLITE_IOERR |
||
308 | # being returned when client-2 attempts a shared lock on the RECOVER byte. |
||
309 | # |
||
310 | # An SQLITE_BUSY should be converted to an SQLITE_BUSY_RECOVERY. An |
||
311 | # SQLITE_IOERR should be returned to the caller. |
||
312 | # |
||
313 | do_test wal3-5.1 { |
||
314 | faultsim_delete_and_reopen |
||
315 | execsql { |
||
316 | PRAGMA journal_mode = WAL; |
||
317 | CREATE TABLE t1(a, b); |
||
318 | INSERT INTO t1 VALUES(1, 2); |
||
319 | INSERT INTO t1 VALUES(3, 4); |
||
320 | } |
||
321 | faultsim_save_and_close |
||
322 | } {} |
||
323 | |||
324 | testvfs T -default 1 |
||
325 | T script method_callback |
||
326 | |||
327 | proc method_callback {method args} { |
||
328 | if {$method == "xShmBarrier"} { |
||
329 | incr ::barrier_count |
||
330 | if {$::barrier_count == 2} { |
||
331 | # This code is executed within the xShmBarrier() callback invoked |
||
332 | # by the client running recovery as part of writing the recovered |
||
333 | # wal-index header. If a second client attempts to access the |
||
334 | # database now, it reads a corrupt (partially written) wal-index |
||
335 | # header. But it cannot even get that far, as the first client |
||
336 | # is still holding all the locks (recovery takes an exclusive lock |
||
337 | # on *all* db locks, preventing access by any other client). |
||
338 | # |
||
339 | # If global variable ::wal3_do_lockfailure is non-zero, then set |
||
340 | # things up so that an IO error occurs within an xShmLock() callback |
||
341 | # made by the second client (aka [db2]). |
||
342 | # |
||
343 | sqlite3 db2 test.db |
||
344 | if { $::wal3_do_lockfailure } { T filter xShmLock } |
||
345 | set ::testrc [ catch { db2 eval "SELECT * FROM t1" } ::testmsg ] |
||
346 | T filter {} |
||
347 | db2 close |
||
348 | } |
||
349 | } |
||
350 | |||
351 | if {$method == "xShmLock"} { |
||
352 | foreach {file handle spec} $args break |
||
353 | if { $spec == "2 1 lock shared" } { |
||
354 | return SQLITE_IOERR |
||
355 | } |
||
356 | } |
||
357 | |||
358 | return SQLITE_OK |
||
359 | } |
||
360 | |||
361 | # Test a normal SQLITE_BUSY return. |
||
362 | # |
||
363 | T filter xShmBarrier |
||
364 | set testrc "" |
||
365 | set testmsg "" |
||
366 | set barrier_count 0 |
||
367 | set wal3_do_lockfailure 0 |
||
368 | do_test wal3-5.2 { |
||
369 | faultsim_restore_and_reopen |
||
370 | execsql { SELECT * FROM t1 } |
||
371 | } {1 2 3 4} |
||
372 | do_test wal3-5.3 { |
||
373 | list $::testrc $::testmsg |
||
374 | } {1 {database is locked}} |
||
375 | db close |
||
376 | |||
377 | # Test an SQLITE_IOERR return. |
||
378 | # |
||
379 | T filter xShmBarrier |
||
380 | set barrier_count 0 |
||
381 | set wal3_do_lockfailure 1 |
||
382 | set testrc "" |
||
383 | set testmsg "" |
||
384 | do_test wal3-5.4 { |
||
385 | faultsim_restore_and_reopen |
||
386 | execsql { SELECT * FROM t1 } |
||
387 | } {1 2 3 4} |
||
388 | do_test wal3-5.5 { |
||
389 | list $::testrc $::testmsg |
||
390 | } {1 {disk I/O error}} |
||
391 | |||
392 | db close |
||
393 | T delete |
||
394 | |||
395 | #------------------------------------------------------------------------- |
||
396 | # When opening a read-transaction on a database, if the entire log has |
||
397 | # already been copied to the database file, the reader grabs a special |
||
398 | # kind of read lock (on aReadMark[0]). This set of test cases tests the |
||
399 | # outcome of the following: |
||
400 | # |
||
401 | # + The reader discovering that between the time when it determined |
||
402 | # that the log had been completely backfilled and the lock is obtained |
||
403 | # that a writer has written to the log. In this case the reader should |
||
404 | # acquire a different read-lock (not aReadMark[0]) and read the new |
||
405 | # snapshot. |
||
406 | # |
||
407 | # + The attempt to obtain the lock on aReadMark[0] fails with SQLITE_BUSY. |
||
408 | # This can happen if a checkpoint is ongoing. In this case also simply |
||
409 | # obtain a different read-lock. |
||
410 | # |
||
411 | catch {db close} |
||
412 | testvfs T -default 1 |
||
413 | do_test wal3-6.1.1 { |
||
414 | file delete -force test.db test.db-journal test.db wal |
||
415 | sqlite3 db test.db |
||
416 | execsql { PRAGMA auto_vacuum = off } |
||
417 | execsql { PRAGMA journal_mode = WAL } |
||
418 | execsql { |
||
419 | CREATE TABLE t1(a, b); |
||
420 | INSERT INTO t1 VALUES('o', 't'); |
||
421 | INSERT INTO t1 VALUES('t', 'f'); |
||
422 | } |
||
423 | } {} |
||
424 | do_test wal3-6.1.2 { |
||
425 | sqlite3 db2 test.db |
||
426 | sqlite3 db3 test.db |
||
427 | execsql { BEGIN ; SELECT * FROM t1 } db3 |
||
428 | } {o t t f} |
||
429 | do_test wal3-6.1.3 { |
||
430 | execsql { PRAGMA wal_checkpoint } db2 |
||
431 | } {0 7 7} |
||
432 | |||
433 | # At this point the log file has been fully checkpointed. However, |
||
434 | # connection [db3] holds a lock that prevents the log from being wrapped. |
||
435 | # Test case 3.6.1.4 has [db] attempt a read-lock on aReadMark[0]. But |
||
436 | # as it is obtaining the lock, [db2] appends to the log file. |
||
437 | # |
||
438 | T filter xShmLock |
||
439 | T script lock_callback |
||
440 | proc lock_callback {method file handle spec} { |
||
441 | if {$spec == "3 1 lock shared"} { |
||
442 | # This is the callback for [db] to obtain the read lock on aReadMark[0]. |
||
443 | # Disable future callbacks using [T filter {}] and write to the log |
||
444 | # file using [db2]. [db3] is preventing [db2] from wrapping the log |
||
445 | # here, so this is an append. |
||
446 | T filter {} |
||
447 | db2 eval { INSERT INTO t1 VALUES('f', 's') } |
||
448 | } |
||
449 | return SQLITE_OK |
||
450 | } |
||
451 | do_test wal3-6.1.4 { |
||
452 | execsql { |
||
453 | BEGIN; |
||
454 | SELECT * FROM t1; |
||
455 | } |
||
456 | } {o t t f f s} |
||
457 | |||
458 | # [db] should be left holding a read-lock on some slot other than |
||
459 | # aReadMark[0]. Test this by demonstrating that the read-lock is preventing |
||
460 | # the log from being wrapped. |
||
461 | # |
||
462 | do_test wal3-6.1.5 { |
||
463 | db3 eval COMMIT |
||
464 | db2 eval { PRAGMA wal_checkpoint } |
||
465 | set sz1 [file size test.db-wal] |
||
466 | db2 eval { INSERT INTO t1 VALUES('s', 'e') } |
||
467 | set sz2 [file size test.db-wal] |
||
468 | expr {$sz2>$sz1} |
||
469 | } {1} |
||
470 | |||
471 | # Test that if [db2] had not interfered when [db] was trying to grab |
||
472 | # aReadMark[0], it would have been possible to wrap the log in 3.6.1.5. |
||
473 | # |
||
474 | do_test wal3-6.1.6 { |
||
475 | execsql { COMMIT } |
||
476 | execsql { PRAGMA wal_checkpoint } db2 |
||
477 | execsql { |
||
478 | BEGIN; |
||
479 | SELECT * FROM t1; |
||
480 | } |
||
481 | } {o t t f f s s e} |
||
482 | do_test wal3-6.1.7 { |
||
483 | db2 eval { PRAGMA wal_checkpoint } |
||
484 | set sz1 [file size test.db-wal] |
||
485 | db2 eval { INSERT INTO t1 VALUES('n', 't') } |
||
486 | set sz2 [file size test.db-wal] |
||
487 | expr {$sz2==$sz1} |
||
488 | } {1} |
||
489 | |||
490 | db3 close |
||
491 | db2 close |
||
492 | db close |
||
493 | |||
494 | do_test wal3-6.2.1 { |
||
495 | file delete -force test.db test.db-journal test.db wal |
||
496 | sqlite3 db test.db |
||
497 | sqlite3 db2 test.db |
||
498 | execsql { PRAGMA auto_vacuum = off } |
||
499 | execsql { PRAGMA journal_mode = WAL } |
||
500 | execsql { |
||
501 | CREATE TABLE t1(a, b); |
||
502 | INSERT INTO t1 VALUES('h', 'h'); |
||
503 | INSERT INTO t1 VALUES('l', 'b'); |
||
504 | } |
||
505 | } {} |
||
506 | |||
507 | T filter xShmLock |
||
508 | T script lock_callback |
||
509 | proc lock_callback {method file handle spec} { |
||
510 | if {$spec == "3 1 unlock exclusive"} { |
||
511 | T filter {} |
||
512 | set ::R [db2 eval { |
||
513 | BEGIN; |
||
514 | SELECT * FROM t1; |
||
515 | }] |
||
516 | } |
||
517 | } |
||
518 | do_test wal3-6.2.2 { |
||
519 | execsql { PRAGMA wal_checkpoint } |
||
520 | } {0 7 7} |
||
521 | do_test wal3-6.2.3 { |
||
522 | set ::R |
||
523 | } {h h l b} |
||
524 | do_test wal3-6.2.4 { |
||
525 | set sz1 [file size test.db-wal] |
||
526 | execsql { INSERT INTO t1 VALUES('b', 'c'); } |
||
527 | set sz2 [file size test.db-wal] |
||
528 | expr {$sz2 > $sz1} |
||
529 | } {1} |
||
530 | do_test wal3-6.2.5 { |
||
531 | db2 eval { COMMIT } |
||
532 | execsql { PRAGMA wal_checkpoint } |
||
533 | set sz1 [file size test.db-wal] |
||
534 | execsql { INSERT INTO t1 VALUES('n', 'o'); } |
||
535 | set sz2 [file size test.db-wal] |
||
536 | expr {$sz2 == $sz1} |
||
537 | } {1} |
||
538 | |||
539 | db2 close |
||
540 | db close |
||
541 | T delete |
||
542 | |||
543 | #------------------------------------------------------------------------- |
||
544 | # When opening a read-transaction on a database, if the entire log has |
||
545 | # not yet been copied to the database file, the reader grabs a read |
||
546 | # lock on aReadMark[x], where x>0. The following test cases experiment |
||
547 | # with the outcome of the following: |
||
548 | # |
||
549 | # + The reader discovering that between the time when it read the |
||
550 | # wal-index header and the lock was obtained that a writer has |
||
551 | # written to the log. In this case the reader should re-read the |
||
552 | # wal-index header and lock a snapshot corresponding to the new |
||
553 | # header. |
||
554 | # |
||
555 | # + The value in the aReadMark[x] slot has been modified since it was |
||
556 | # read. |
||
557 | # |
||
558 | catch {db close} |
||
559 | testvfs T -default 1 |
||
560 | do_test wal3-7.1.1 { |
||
561 | file delete -force test.db test.db-journal test.db wal |
||
562 | sqlite3 db test.db |
||
563 | execsql { |
||
564 | PRAGMA journal_mode = WAL; |
||
565 | CREATE TABLE blue(red PRIMARY KEY, green); |
||
566 | } |
||
567 | } {wal} |
||
568 | |||
569 | T script method_callback |
||
570 | T filter xOpen |
||
571 | proc method_callback {method args} { |
||
572 | if {$method == "xOpen"} { return "reader" } |
||
573 | } |
||
574 | do_test wal3-7.1.2 { |
||
575 | sqlite3 db2 test.db |
||
576 | execsql { SELECT * FROM blue } db2 |
||
577 | } {} |
||
578 | |||
579 | T filter xShmLock |
||
580 | set ::locks [list] |
||
581 | proc method_callback {method file handle spec} { |
||
582 | if {$handle != "reader" } { return } |
||
583 | if {$method == "xShmLock"} { |
||
584 | catch { execsql { INSERT INTO blue VALUES(1, 2) } } |
||
585 | catch { execsql { INSERT INTO blue VALUES(3, 4) } } |
||
586 | } |
||
587 | lappend ::locks $spec |
||
588 | } |
||
589 | do_test wal3-7.1.3 { |
||
590 | execsql { SELECT * FROM blue } db2 |
||
591 | } {1 2 3 4} |
||
592 | do_test wal3-7.1.4 { |
||
593 | set ::locks |
||
594 | } {{4 1 lock shared} {4 1 unlock shared} {5 1 lock shared} {5 1 unlock shared}} |
||
595 | |||
596 | set ::locks [list] |
||
597 | proc method_callback {method file handle spec} { |
||
598 | if {$handle != "reader" } { return } |
||
599 | if {$method == "xShmLock"} { |
||
600 | catch { execsql { INSERT INTO blue VALUES(5, 6) } } |
||
601 | } |
||
602 | lappend ::locks $spec |
||
603 | } |
||
604 | do_test wal3-7.2.1 { |
||
605 | execsql { SELECT * FROM blue } db2 |
||
606 | } {1 2 3 4 5 6} |
||
607 | do_test wal3-7.2.2 { |
||
608 | set ::locks |
||
609 | } {{5 1 lock shared} {5 1 unlock shared} {4 1 lock shared} {4 1 unlock shared}} |
||
610 | |||
611 | db close |
||
612 | db2 close |
||
613 | T delete |
||
614 | |||
615 | #------------------------------------------------------------------------- |
||
616 | # |
||
617 | do_test wal3-8.1 { |
||
618 | file delete -force test.db test.db-journal test.db wal |
||
619 | sqlite3 db test.db |
||
620 | sqlite3 db2 test.db |
||
621 | execsql { |
||
622 | PRAGMA auto_vacuum = off; |
||
623 | PRAGMA journal_mode = WAL; |
||
624 | CREATE TABLE b(c); |
||
625 | INSERT INTO b VALUES('Tehran'); |
||
626 | INSERT INTO b VALUES('Qom'); |
||
627 | INSERT INTO b VALUES('Markazi'); |
||
628 | PRAGMA wal_checkpoint; |
||
629 | } |
||
630 | } {wal 0 9 9} |
||
631 | do_test wal3-8.2 { |
||
632 | execsql { SELECT * FROM b } |
||
633 | } {Tehran Qom Markazi} |
||
634 | do_test wal3-8.3 { |
||
635 | db eval { SELECT * FROM b } { |
||
636 | db eval { INSERT INTO b VALUES('Qazvin') } |
||
637 | set r [db2 eval { SELECT * FROM b }] |
||
638 | break |
||
639 | } |
||
640 | set r |
||
641 | } {Tehran Qom Markazi Qazvin} |
||
642 | do_test wal3-8.4 { |
||
643 | execsql { |
||
644 | INSERT INTO b VALUES('Gilan'); |
||
645 | INSERT INTO b VALUES('Ardabil'); |
||
646 | } |
||
647 | } {} |
||
648 | db2 close |
||
649 | |||
650 | faultsim_save_and_close |
||
651 | testvfs T -default 1 |
||
652 | faultsim_restore_and_reopen |
||
653 | T filter xShmLock |
||
654 | T script lock_callback |
||
655 | |||
656 | proc lock_callback {method file handle spec} { |
||
657 | if {$spec == "4 1 unlock exclusive"} { |
||
658 | T filter {} |
||
659 | set ::r [catchsql { SELECT * FROM b } db2] |
||
660 | } |
||
661 | } |
||
662 | sqlite3 db test.db |
||
663 | sqlite3 db2 test.db |
||
664 | do_test wal3-8.5 { |
||
665 | execsql { SELECT * FROM b } |
||
666 | } {Tehran Qom Markazi Qazvin Gilan Ardabil} |
||
667 | do_test wal3-8.6 { |
||
668 | set ::r |
||
669 | } {1 {locking protocol}} |
||
670 | |||
671 | db close |
||
672 | db2 close |
||
673 | |||
674 | faultsim_restore_and_reopen |
||
675 | sqlite3 db2 test.db |
||
676 | T filter xShmLock |
||
677 | T script lock_callback |
||
678 | proc lock_callback {method file handle spec} { |
||
679 | if {$spec == "1 7 unlock exclusive"} { |
||
680 | T filter {} |
||
681 | set ::r [catchsql { SELECT * FROM b } db2] |
||
682 | } |
||
683 | } |
||
684 | unset ::r |
||
685 | do_test wal3-8.5 { |
||
686 | execsql { SELECT * FROM b } |
||
687 | } {Tehran Qom Markazi Qazvin Gilan Ardabil} |
||
688 | do_test wal3-8.6 { |
||
689 | set ::r |
||
690 | } {1 {locking protocol}} |
||
691 | |||
692 | db close |
||
693 | db2 close |
||
694 | T delete |
||
695 | |||
696 | #------------------------------------------------------------------------- |
||
697 | # When a connection opens a read-lock on the database, it searches for |
||
698 | # an aReadMark[] slot that is already set to the mxFrame value for the |
||
699 | # new transaction. If it cannot find one, it attempts to obtain an |
||
700 | # exclusive lock on an aReadMark[] slot for the purposes of modifying |
||
701 | # the value, then drops back to a shared-lock for the duration of the |
||
702 | # transaction. |
||
703 | # |
||
704 | # This test case verifies that if an exclusive lock cannot be obtained |
||
705 | # on any aReadMark[] slot (because there are already several readers), |
||
706 | # the client takes a shared-lock on a slot without modifying the value |
||
707 | # and continues. |
||
708 | # |
||
709 | set nConn 50 |
||
710 | if { [string match *BSD $tcl_platform(os)] } { set nConn 35 } |
||
711 | do_test wal3-9.0 { |
||
712 | file delete -force test.db test.db-journal test.db wal |
||
713 | sqlite3 db test.db |
||
714 | execsql { |
||
715 | PRAGMA page_size = 1024; |
||
716 | PRAGMA journal_mode = WAL; |
||
717 | CREATE TABLE whoami(x); |
||
718 | INSERT INTO whoami VALUES('nobody'); |
||
719 | } |
||
720 | } {wal} |
||
721 | for {set i 0} {$i < $nConn} {incr i} { |
||
722 | set c db$i |
||
723 | do_test wal3-9.1.$i { |
||
724 | sqlite3 $c test.db |
||
725 | execsql { UPDATE whoami SET x = $c } |
||
726 | execsql { |
||
727 | BEGIN; |
||
728 | SELECT * FROM whoami |
||
729 | } $c |
||
730 | } $c |
||
731 | } |
||
732 | for {set i 0} {$i < $nConn} {incr i} { |
||
733 | set c db$i |
||
734 | do_test wal3-9.2.$i { |
||
735 | execsql { SELECT * FROM whoami } $c |
||
736 | } $c |
||
737 | } |
||
738 | |||
739 | set sz [expr 1024 * (2+$AUTOVACUUM)] |
||
740 | do_test wal3-9.3 { |
||
741 | for {set i 0} {$i < ($nConn-1)} {incr i} { db$i close } |
||
742 | execsql { PRAGMA wal_checkpoint } |
||
743 | byte_is_zero test.db [expr $sz-1024] |
||
744 | } {1} |
||
745 | do_test wal3-9.4 { |
||
746 | db[expr $nConn-1] close |
||
747 | execsql { PRAGMA wal_checkpoint } |
||
748 | set sz2 [file size test.db] |
||
749 | byte_is_zero test.db [expr $sz-1024] |
||
750 | } {0} |
||
751 | |||
752 | do_multiclient_test tn { |
||
753 | do_test wal3-10.$tn.1 { |
||
754 | sql1 { |
||
755 | PRAGMA page_size = 1024; |
||
756 | CREATE TABLE t1(x); |
||
757 | PRAGMA journal_mode = WAL; |
||
758 | PRAGMA wal_autocheckpoint = 100000; |
||
759 | BEGIN; |
||
760 | INSERT INTO t1 VALUES(randomblob(800)); |
||
761 | INSERT INTO t1 SELECT randomblob(800) FROM t1; -- 2 |
||
762 | INSERT INTO t1 SELECT randomblob(800) FROM t1; -- 4 |
||
763 | INSERT INTO t1 SELECT randomblob(800) FROM t1; -- 8 |
||
764 | INSERT INTO t1 SELECT randomblob(800) FROM t1; -- 16 |
||
765 | INSERT INTO t1 SELECT randomblob(800) FROM t1; -- 32 |
||
766 | INSERT INTO t1 SELECT randomblob(800) FROM t1; -- 64 |
||
767 | INSERT INTO t1 SELECT randomblob(800) FROM t1; -- 128 |
||
768 | INSERT INTO t1 SELECT randomblob(800) FROM t1; -- 256 |
||
769 | INSERT INTO t1 SELECT randomblob(800) FROM t1; -- 512 |
||
770 | INSERT INTO t1 SELECT randomblob(800) FROM t1; -- 1024 |
||
771 | INSERT INTO t1 SELECT randomblob(800) FROM t1; -- 2048 |
||
772 | INSERT INTO t1 SELECT randomblob(800) FROM t1; -- 4096 |
||
773 | INSERT INTO t1 SELECT randomblob(800) FROM t1; -- 8192 |
||
774 | COMMIT; |
||
775 | CREATE INDEX i1 ON t1(x); |
||
776 | } |
||
777 | |||
778 | expr {[file size test.db-wal] > [expr 1032*9000]} |
||
779 | } 1 |
||
780 | |||
781 | do_test wal3-10.$tn.2 { |
||
782 | sql2 {PRAGMA integrity_check} |
||
783 | } {ok} |
||
784 | } |
||
785 | |||
786 | finish_test |
||
787 |