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: forcecopy test.db test2.db
98: forcecopy test.db-wal test2.db-wal
99: forcedelete 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: forcedelete 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: execsql { CREATE TABLE filler(a,b,c); }
221:
222: set ::syncs [list]
223: T filter xSync
224: execsql {
225: CREATE TABLE x(y);
226: INSERT INTO x VALUES('z');
227: PRAGMA wal_checkpoint;
228: }
229: T filter {}
230: set ::syncs
231: } $synccount
232:
233: db close
234: T delete
235: }
236:
237: #-------------------------------------------------------------------------
238: # When recovering the contents of a WAL file, a process obtains the WRITER
239: # lock, then locks all other bytes before commencing recovery. If it fails
240: # to lock all other bytes (because some other process is holding a read
241: # lock) it should retry up to 100 times. Then return SQLITE_PROTOCOL to the
242: # caller. Test this (test case wal3-4.3).
243: #
244: # Also test the effect of hitting an SQLITE_BUSY while attempting to obtain
245: # the WRITER lock (should be the same). Test case wal3-4.4.
246: #
247: proc lock_callback {method filename handle lock} {
248: lappend ::locks $lock
249: }
250: do_test wal3-4.1 {
251: testvfs T
252: T filter xShmLock
253: T script lock_callback
254: set ::locks [list]
255: sqlite3 db test.db -vfs T
256: execsql { SELECT * FROM x }
257: lrange $::locks 0 3
258: } [list {0 1 lock exclusive} {1 7 lock exclusive} \
259: {1 7 unlock exclusive} {0 1 unlock exclusive} \
260: ]
261: do_test wal3-4.2 {
262: db close
263: set ::locks [list]
264: sqlite3 db test.db -vfs T
265: execsql { SELECT * FROM x }
266: lrange $::locks 0 3
267: } [list {0 1 lock exclusive} {1 7 lock exclusive} \
268: {1 7 unlock exclusive} {0 1 unlock exclusive} \
269: ]
270: proc lock_callback {method filename handle lock} {
271: if {$lock == "1 7 lock exclusive"} { return SQLITE_BUSY }
272: return SQLITE_OK
273: }
274: puts " Warning: This next test case causes SQLite to call xSleep(1) 100 times."
275: puts " Normally this equates to a 100ms delay, but if SQLite is built on unix"
276: puts " without HAVE_USLEEP defined, it may be 100 seconds."
277: do_test wal3-4.3 {
278: db close
279: set ::locks [list]
280: sqlite3 db test.db -vfs T
281: catchsql { SELECT * FROM x }
282: } {1 {locking protocol}}
283:
284: puts " Warning: Same again!"
285: proc lock_callback {method filename handle lock} {
286: if {$lock == "0 1 lock exclusive"} { return SQLITE_BUSY }
287: return SQLITE_OK
288: }
289: do_test wal3-4.4 {
290: db close
291: set ::locks [list]
292: sqlite3 db test.db -vfs T
293: catchsql { SELECT * FROM x }
294: } {1 {locking protocol}}
295: db close
296: T delete
297:
298:
299: #-------------------------------------------------------------------------
300: # Only one client may run recovery at a time. Test this mechanism.
301: #
302: # When client-2 tries to open a read transaction while client-1 is
303: # running recovery, it fails to obtain a lock on an aReadMark[] slot
304: # (because they are all locked by recovery). It then tries to obtain
305: # a shared lock on the RECOVER lock to see if there really is a
306: # recovery running or not.
307: #
308: # This block of tests checks the effect of an SQLITE_BUSY or SQLITE_IOERR
309: # being returned when client-2 attempts a shared lock on the RECOVER byte.
310: #
311: # An SQLITE_BUSY should be converted to an SQLITE_BUSY_RECOVERY. An
312: # SQLITE_IOERR should be returned to the caller.
313: #
314: do_test wal3-5.1 {
315: faultsim_delete_and_reopen
316: execsql {
317: PRAGMA journal_mode = WAL;
318: CREATE TABLE t1(a, b);
319: INSERT INTO t1 VALUES(1, 2);
320: INSERT INTO t1 VALUES(3, 4);
321: }
322: faultsim_save_and_close
323: } {}
324:
325: testvfs T -default 1
326: T script method_callback
327:
328: proc method_callback {method args} {
329: if {$method == "xShmBarrier"} {
330: incr ::barrier_count
331: if {$::barrier_count == 2} {
332: # This code is executed within the xShmBarrier() callback invoked
333: # by the client running recovery as part of writing the recovered
334: # wal-index header. If a second client attempts to access the
335: # database now, it reads a corrupt (partially written) wal-index
336: # header. But it cannot even get that far, as the first client
337: # is still holding all the locks (recovery takes an exclusive lock
338: # on *all* db locks, preventing access by any other client).
339: #
340: # If global variable ::wal3_do_lockfailure is non-zero, then set
341: # things up so that an IO error occurs within an xShmLock() callback
342: # made by the second client (aka [db2]).
343: #
344: sqlite3 db2 test.db
345: if { $::wal3_do_lockfailure } { T filter xShmLock }
346: set ::testrc [ catch { db2 eval "SELECT * FROM t1" } ::testmsg ]
347: T filter {}
348: db2 close
349: }
350: }
351:
352: if {$method == "xShmLock"} {
353: foreach {file handle spec} $args break
354: if { $spec == "2 1 lock shared" } {
355: return SQLITE_IOERR
356: }
357: }
358:
359: return SQLITE_OK
360: }
361:
362: # Test a normal SQLITE_BUSY return.
363: #
364: T filter xShmBarrier
365: set testrc ""
366: set testmsg ""
367: set barrier_count 0
368: set wal3_do_lockfailure 0
369: do_test wal3-5.2 {
370: faultsim_restore_and_reopen
371: execsql { SELECT * FROM t1 }
372: } {1 2 3 4}
373: do_test wal3-5.3 {
374: list $::testrc $::testmsg
375: } {1 {database is locked}}
376: db close
377:
378: # Test an SQLITE_IOERR return.
379: #
380: T filter xShmBarrier
381: set barrier_count 0
382: set wal3_do_lockfailure 1
383: set testrc ""
384: set testmsg ""
385: do_test wal3-5.4 {
386: faultsim_restore_and_reopen
387: execsql { SELECT * FROM t1 }
388: } {1 2 3 4}
389: do_test wal3-5.5 {
390: list $::testrc $::testmsg
391: } {1 {disk I/O error}}
392:
393: db close
394: T delete
395:
396: #-------------------------------------------------------------------------
397: # When opening a read-transaction on a database, if the entire log has
398: # already been copied to the database file, the reader grabs a special
399: # kind of read lock (on aReadMark[0]). This set of test cases tests the
400: # outcome of the following:
401: #
402: # + The reader discovering that between the time when it determined
403: # that the log had been completely backfilled and the lock is obtained
404: # that a writer has written to the log. In this case the reader should
405: # acquire a different read-lock (not aReadMark[0]) and read the new
406: # snapshot.
407: #
408: # + The attempt to obtain the lock on aReadMark[0] fails with SQLITE_BUSY.
409: # This can happen if a checkpoint is ongoing. In this case also simply
410: # obtain a different read-lock.
411: #
412: catch {db close}
413: testvfs T -default 1
414: do_test wal3-6.1.1 {
415: forcedelete test.db test.db-journal test.db wal
416: sqlite3 db test.db
417: execsql { PRAGMA auto_vacuum = off }
418: execsql { PRAGMA journal_mode = WAL }
419: execsql {
420: CREATE TABLE t1(a, b);
421: INSERT INTO t1 VALUES('o', 't');
422: INSERT INTO t1 VALUES('t', 'f');
423: }
424: } {}
425: do_test wal3-6.1.2 {
426: sqlite3 db2 test.db
427: sqlite3 db3 test.db
428: execsql { BEGIN ; SELECT * FROM t1 } db3
429: } {o t t f}
430: do_test wal3-6.1.3 {
431: execsql { PRAGMA wal_checkpoint } db2
432: } {0 4 4}
433:
434: # At this point the log file has been fully checkpointed. However,
435: # connection [db3] holds a lock that prevents the log from being wrapped.
436: # Test case 3.6.1.4 has [db] attempt a read-lock on aReadMark[0]. But
437: # as it is obtaining the lock, [db2] appends to the log file.
438: #
439: T filter xShmLock
440: T script lock_callback
441: proc lock_callback {method file handle spec} {
442: if {$spec == "3 1 lock shared"} {
443: # This is the callback for [db] to obtain the read lock on aReadMark[0].
444: # Disable future callbacks using [T filter {}] and write to the log
445: # file using [db2]. [db3] is preventing [db2] from wrapping the log
446: # here, so this is an append.
447: T filter {}
448: db2 eval { INSERT INTO t1 VALUES('f', 's') }
449: }
450: return SQLITE_OK
451: }
452: do_test wal3-6.1.4 {
453: execsql {
454: BEGIN;
455: SELECT * FROM t1;
456: }
457: } {o t t f f s}
458:
459: # [db] should be left holding a read-lock on some slot other than
460: # aReadMark[0]. Test this by demonstrating that the read-lock is preventing
461: # the log from being wrapped.
462: #
463: do_test wal3-6.1.5 {
464: db3 eval COMMIT
465: db2 eval { PRAGMA wal_checkpoint }
466: set sz1 [file size test.db-wal]
467: db2 eval { INSERT INTO t1 VALUES('s', 'e') }
468: set sz2 [file size test.db-wal]
469: expr {$sz2>$sz1}
470: } {1}
471:
472: # Test that if [db2] had not interfered when [db] was trying to grab
473: # aReadMark[0], it would have been possible to wrap the log in 3.6.1.5.
474: #
475: do_test wal3-6.1.6 {
476: execsql { COMMIT }
477: execsql { PRAGMA wal_checkpoint } db2
478: execsql {
479: BEGIN;
480: SELECT * FROM t1;
481: }
482: } {o t t f f s s e}
483: do_test wal3-6.1.7 {
484: db2 eval { PRAGMA wal_checkpoint }
485: set sz1 [file size test.db-wal]
486: db2 eval { INSERT INTO t1 VALUES('n', 't') }
487: set sz2 [file size test.db-wal]
488: expr {$sz2==$sz1}
489: } {1}
490:
491: db3 close
492: db2 close
493: db close
494:
495: do_test wal3-6.2.1 {
496: forcedelete test.db test.db-journal test.db wal
497: sqlite3 db test.db
498: sqlite3 db2 test.db
499: execsql { PRAGMA auto_vacuum = off }
500: execsql { PRAGMA journal_mode = WAL }
501: execsql {
502: CREATE TABLE t1(a, b);
503: INSERT INTO t1 VALUES('h', 'h');
504: INSERT INTO t1 VALUES('l', 'b');
505: }
506: } {}
507:
508: T filter xShmLock
509: T script lock_callback
510: proc lock_callback {method file handle spec} {
511: if {$spec == "3 1 unlock exclusive"} {
512: T filter {}
513: set ::R [db2 eval {
514: BEGIN;
515: SELECT * FROM t1;
516: }]
517: }
518: }
519: do_test wal3-6.2.2 {
520: execsql { PRAGMA wal_checkpoint }
521: } {0 4 4}
522: do_test wal3-6.2.3 {
523: set ::R
524: } {h h l b}
525: do_test wal3-6.2.4 {
526: set sz1 [file size test.db-wal]
527: execsql { INSERT INTO t1 VALUES('b', 'c'); }
528: set sz2 [file size test.db-wal]
529: expr {$sz2 > $sz1}
530: } {1}
531: do_test wal3-6.2.5 {
532: db2 eval { COMMIT }
533: execsql { PRAGMA wal_checkpoint }
534: set sz1 [file size test.db-wal]
535: execsql { INSERT INTO t1 VALUES('n', 'o'); }
536: set sz2 [file size test.db-wal]
537: expr {$sz2 == $sz1}
538: } {1}
539:
540: db2 close
541: db close
542: T delete
543:
544: #-------------------------------------------------------------------------
545: # When opening a read-transaction on a database, if the entire log has
546: # not yet been copied to the database file, the reader grabs a read
547: # lock on aReadMark[x], where x>0. The following test cases experiment
548: # with the outcome of the following:
549: #
550: # + The reader discovering that between the time when it read the
551: # wal-index header and the lock was obtained that a writer has
552: # written to the log. In this case the reader should re-read the
553: # wal-index header and lock a snapshot corresponding to the new
554: # header.
555: #
556: # + The value in the aReadMark[x] slot has been modified since it was
557: # read.
558: #
559: catch {db close}
560: testvfs T -default 1
561: do_test wal3-7.1.1 {
562: forcedelete test.db test.db-journal test.db wal
563: sqlite3 db test.db
564: execsql {
565: PRAGMA journal_mode = WAL;
566: CREATE TABLE blue(red PRIMARY KEY, green);
567: }
568: } {wal}
569:
570: T script method_callback
571: T filter xOpen
572: proc method_callback {method args} {
573: if {$method == "xOpen"} { return "reader" }
574: }
575: do_test wal3-7.1.2 {
576: sqlite3 db2 test.db
577: execsql { SELECT * FROM blue } db2
578: } {}
579:
580: T filter xShmLock
581: set ::locks [list]
582: proc method_callback {method file handle spec} {
583: if {$handle != "reader" } { return }
584: if {$method == "xShmLock"} {
585: catch { execsql { INSERT INTO blue VALUES(1, 2) } }
586: catch { execsql { INSERT INTO blue VALUES(3, 4) } }
587: }
588: lappend ::locks $spec
589: }
590: do_test wal3-7.1.3 {
591: execsql { SELECT * FROM blue } db2
592: } {1 2 3 4}
593: do_test wal3-7.1.4 {
594: set ::locks
595: } {{4 1 lock shared} {4 1 unlock shared} {5 1 lock shared} {5 1 unlock shared}}
596:
597: set ::locks [list]
598: proc method_callback {method file handle spec} {
599: if {$handle != "reader" } { return }
600: if {$method == "xShmLock"} {
601: catch { execsql { INSERT INTO blue VALUES(5, 6) } }
602: }
603: lappend ::locks $spec
604: }
605: do_test wal3-7.2.1 {
606: execsql { SELECT * FROM blue } db2
607: } {1 2 3 4 5 6}
608: do_test wal3-7.2.2 {
609: set ::locks
610: } {{5 1 lock shared} {5 1 unlock shared} {4 1 lock shared} {4 1 unlock shared}}
611:
612: db close
613: db2 close
614: T delete
615:
616: #-------------------------------------------------------------------------
617: #
618: do_test wal3-8.1 {
619: forcedelete test.db test.db-journal test.db wal
620: sqlite3 db test.db
621: sqlite3 db2 test.db
622: execsql {
623: PRAGMA auto_vacuum = off;
624: PRAGMA journal_mode = WAL;
625: CREATE TABLE b(c);
626: INSERT INTO b VALUES('Tehran');
627: INSERT INTO b VALUES('Qom');
628: INSERT INTO b VALUES('Markazi');
629: PRAGMA wal_checkpoint;
630: }
631: } {wal 0 5 5}
632: do_test wal3-8.2 {
633: execsql { SELECT * FROM b }
634: } {Tehran Qom Markazi}
635: do_test wal3-8.3 {
636: db eval { SELECT * FROM b } {
637: db eval { INSERT INTO b VALUES('Qazvin') }
638: set r [db2 eval { SELECT * FROM b }]
639: break
640: }
641: set r
642: } {Tehran Qom Markazi Qazvin}
643: do_test wal3-8.4 {
644: execsql {
645: INSERT INTO b VALUES('Gilan');
646: INSERT INTO b VALUES('Ardabil');
647: }
648: } {}
649: db2 close
650:
651: faultsim_save_and_close
652: testvfs T -default 1
653: faultsim_restore_and_reopen
654: T filter xShmLock
655: T script lock_callback
656:
657: proc lock_callback {method file handle spec} {
658: if {$spec == "4 1 unlock exclusive"} {
659: T filter {}
660: set ::r [catchsql { SELECT * FROM b } db2]
661: }
662: }
663: sqlite3 db test.db
664: sqlite3 db2 test.db
665: do_test wal3-8.5 {
666: execsql { SELECT * FROM b }
667: } {Tehran Qom Markazi Qazvin Gilan Ardabil}
668: do_test wal3-8.6 {
669: set ::r
670: } {1 {locking protocol}}
671:
672: db close
673: db2 close
674:
675: faultsim_restore_and_reopen
676: sqlite3 db2 test.db
677: T filter xShmLock
678: T script lock_callback
679: proc lock_callback {method file handle spec} {
680: if {$spec == "1 7 unlock exclusive"} {
681: T filter {}
682: set ::r [catchsql { SELECT * FROM b } db2]
683: }
684: }
685: unset ::r
686: do_test wal3-8.5 {
687: execsql { SELECT * FROM b }
688: } {Tehran Qom Markazi Qazvin Gilan Ardabil}
689: do_test wal3-8.6 {
690: set ::r
691: } {1 {locking protocol}}
692:
693: db close
694: db2 close
695: T delete
696:
697: #-------------------------------------------------------------------------
698: # When a connection opens a read-lock on the database, it searches for
699: # an aReadMark[] slot that is already set to the mxFrame value for the
700: # new transaction. If it cannot find one, it attempts to obtain an
701: # exclusive lock on an aReadMark[] slot for the purposes of modifying
702: # the value, then drops back to a shared-lock for the duration of the
703: # transaction.
704: #
705: # This test case verifies that if an exclusive lock cannot be obtained
706: # on any aReadMark[] slot (because there are already several readers),
707: # the client takes a shared-lock on a slot without modifying the value
708: # and continues.
709: #
710: set nConn 50
711: if { [string match *BSD $tcl_platform(os)] } { set nConn 25 }
712: do_test wal3-9.0 {
713: forcedelete test.db test.db-journal test.db wal
714: sqlite3 db test.db
715: execsql {
716: PRAGMA page_size = 1024;
717: PRAGMA journal_mode = WAL;
718: CREATE TABLE whoami(x);
719: INSERT INTO whoami VALUES('nobody');
720: }
721: } {wal}
722: for {set i 0} {$i < $nConn} {incr i} {
723: set c db$i
724: do_test wal3-9.1.$i {
725: sqlite3 $c test.db
726: execsql { UPDATE whoami SET x = $c }
727: execsql {
728: BEGIN;
729: SELECT * FROM whoami
730: } $c
731: } $c
732: }
733: for {set i 0} {$i < $nConn} {incr i} {
734: set c db$i
735: do_test wal3-9.2.$i {
736: execsql { SELECT * FROM whoami } $c
737: } $c
738: }
739:
740: set sz [expr 1024 * (2+$AUTOVACUUM)]
741: do_test wal3-9.3 {
742: for {set i 0} {$i < ($nConn-1)} {incr i} { db$i close }
743: execsql { PRAGMA wal_checkpoint }
744: byte_is_zero test.db [expr $sz-1024]
745: } {1}
746: do_test wal3-9.4 {
747: db[expr $nConn-1] close
748: execsql { PRAGMA wal_checkpoint }
749: set sz2 [file size test.db]
750: byte_is_zero test.db [expr $sz-1024]
751: } {0}
752:
753: do_multiclient_test tn {
754: do_test wal3-10.$tn.1 {
755: sql1 {
756: PRAGMA page_size = 1024;
757: CREATE TABLE t1(x);
758: PRAGMA journal_mode = WAL;
759: PRAGMA wal_autocheckpoint = 100000;
760: BEGIN;
761: INSERT INTO t1 VALUES(randomblob(800));
762: INSERT INTO t1 SELECT randomblob(800) FROM t1; -- 2
763: INSERT INTO t1 SELECT randomblob(800) FROM t1; -- 4
764: INSERT INTO t1 SELECT randomblob(800) FROM t1; -- 8
765: INSERT INTO t1 SELECT randomblob(800) FROM t1; -- 16
766: INSERT INTO t1 SELECT randomblob(800) FROM t1; -- 32
767: INSERT INTO t1 SELECT randomblob(800) FROM t1; -- 64
768: INSERT INTO t1 SELECT randomblob(800) FROM t1; -- 128
769: INSERT INTO t1 SELECT randomblob(800) FROM t1; -- 256
770: INSERT INTO t1 SELECT randomblob(800) FROM t1; -- 512
771: INSERT INTO t1 SELECT randomblob(800) FROM t1; -- 1024
772: INSERT INTO t1 SELECT randomblob(800) FROM t1; -- 2048
773: INSERT INTO t1 SELECT randomblob(800) FROM t1; -- 4096
774: INSERT INTO t1 SELECT randomblob(800) FROM t1; -- 8192
775: COMMIT;
776: CREATE INDEX i1 ON t1(x);
777: }
778:
779: expr {[file size test.db-wal] > [expr 1032*9000]}
780: } 1
781:
782: do_test wal3-10.$tn.2 {
783: sql2 {PRAGMA integrity_check}
784: } {ok}
785: }
786:
787: finish_test
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>