blob: 313c0f36e04f3f6ce5b2da559b5380ecde0d8318 [file] [log] [blame]
dane91a54e2010-06-15 17:44:47 +00001# 2010 June 15
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#
12
13set testdir [file dirname $argv0]
14source $testdir/tester.tcl
15source $testdir/lock_common.tcl
16source $testdir/malloc_common.tcl
dan153eda02010-06-21 07:45:47 +000017source $testdir/wal_common.tcl
dane91a54e2010-06-15 17:44:47 +000018
danb0ac3e32010-06-16 10:55:42 +000019#
20# pager1-1.*: Test inter-process locking (clients in multiple processes).
21#
22# pager1-2.*: Test intra-process locking (multiple clients in this process).
23#
24# pager1-3.*: Savepoint related tests.
25#
dan1f4cb652010-06-18 18:59:49 +000026# pager1-4.*: Hot-journal related tests.
27#
dande4996e2010-06-19 11:30:41 +000028# pager1-5.*: Cases related to multi-file commits.
29#
dan146ed782010-06-19 17:26:37 +000030# pager1-6.*: Cases related to "PRAGMA max_page_count"
31#
dan153eda02010-06-21 07:45:47 +000032# pager1-7.*: Cases specific to "PRAGMA journal_mode=TRUNCATE"
33#
danb0ac3e32010-06-16 10:55:42 +000034
dan53f04f32010-06-16 12:30:10 +000035set a_string_counter 1
36proc a_string {n} {
37 global a_string_counter
38 incr a_string_counter
39 string range [string repeat "${a_string_counter}." $n] 1 $n
40}
41db func a_string a_string
42
dana4a90952010-06-15 19:07:42 +000043do_multiclient_test tn {
dane91a54e2010-06-15 17:44:47 +000044
45 # Create and populate a database table using connection [db]. Check
46 # that connections [db2] and [db3] can see the schema and content.
47 #
48 do_test pager1-$tn.1 {
49 sql1 {
50 CREATE TABLE t1(a PRIMARY KEY, b);
51 CREATE INDEX i1 ON t1(b);
52 INSERT INTO t1 VALUES(1, 'one');
53 INSERT INTO t1 VALUES(2, 'two');
54 }
55 } {}
56 do_test pager1-$tn.2 { sql2 { SELECT * FROM t1 } } {1 one 2 two}
57 do_test pager1-$tn.3 { sql3 { SELECT * FROM t1 } } {1 one 2 two}
58
59 # Open a transaction and add a row using [db]. This puts [db] in
60 # RESERVED state. Check that connections [db2] and [db3] can still
61 # read the database content as it was before the transaction was
62 # opened. [db] should see the inserted row.
63 #
64 do_test pager1-$tn.4 {
65 sql1 {
66 BEGIN;
67 INSERT INTO t1 VALUES(3, 'three');
68 }
69 } {}
70 do_test pager1-$tn.5 { sql2 { SELECT * FROM t1 } } {1 one 2 two}
dane91a54e2010-06-15 17:44:47 +000071 do_test pager1-$tn.7 { sql1 { SELECT * FROM t1 } } {1 one 2 two 3 three}
72
73 # [db] still has an open write transaction. Check that this prevents
74 # other connections (specifically [db2]) from writing to the database.
75 #
76 # Even if [db2] opens a transaction first, it may not write to the
77 # database. After the attempt to write the db within a transaction,
78 # [db2] is left with an open transaction, but not a read-lock on
79 # the main database. So it does not prevent [db] from committing.
80 #
81 do_test pager1-$tn.8 {
82 csql2 { UPDATE t1 SET a = a + 10 }
83 } {1 {database is locked}}
84 do_test pager1-$tn.9 {
85 csql2 {
86 BEGIN;
87 UPDATE t1 SET a = a + 10;
88 }
89 } {1 {database is locked}}
90
91 # Have [db] commit its transactions. Check the other connections can
92 # now see the new database content.
93 #
94 do_test pager1-$tn.10 { sql1 { COMMIT } } {}
95 do_test pager1-$tn.11 { sql1 { SELECT * FROM t1 } } {1 one 2 two 3 three}
96 do_test pager1-$tn.12 { sql2 { SELECT * FROM t1 } } {1 one 2 two 3 three}
97 do_test pager1-$tn.13 { sql3 { SELECT * FROM t1 } } {1 one 2 two 3 three}
98
99 # Check that, as noted above, [db2] really did keep an open transaction
100 # after the attempt to write the database failed.
101 #
102 do_test pager1-$tn.14 {
103 csql2 { BEGIN }
104 } {1 {cannot start a transaction within a transaction}}
105 do_test pager1-$tn.15 { sql2 { ROLLBACK } } {}
106
107 # Have [db2] open a transaction and take a read-lock on the database.
108 # Check that this prevents [db] from writing to the database (outside
109 # of any transaction). After this fails, check that [db3] can read
110 # the db (showing that [db] did not take a PENDING lock etc.)
111 #
112 do_test pager1-$tn.15 {
113 sql2 { BEGIN; SELECT * FROM t1; }
114 } {1 one 2 two 3 three}
115 do_test pager1-$tn.16 {
116 csql1 { UPDATE t1 SET a = a + 10 }
117 } {1 {database is locked}}
118 do_test pager1-$tn.17 { sql3 { SELECT * FROM t1 } } {1 one 2 two 3 three}
119
120 # This time, have [db] open a transaction before writing the database.
121 # This works - [db] gets a RESERVED lock which does not conflict with
122 # the SHARED lock [db2] is holding.
123 #
124 do_test pager1-$tn.18 {
125 sql1 {
126 BEGIN;
127 UPDATE t1 SET a = a + 10;
128 }
129 } {}
130 do_test pager1-$tn-19 {
131 sql1 { PRAGMA lock_status }
132 } {main reserved temp closed}
133 do_test pager1-$tn-20 {
134 sql2 { PRAGMA lock_status }
135 } {main shared temp closed}
136
137 # Check that all connections can still read the database. Only [db] sees
138 # the updated content (as the transaction has not been committed yet).
139 #
140 do_test pager1-$tn.21 { sql1 { SELECT * FROM t1 } } {11 one 12 two 13 three}
141 do_test pager1-$tn.22 { sql2 { SELECT * FROM t1 } } {1 one 2 two 3 three}
142 do_test pager1-$tn.23 { sql3 { SELECT * FROM t1 } } {1 one 2 two 3 three}
143
144 # Because [db2] still has the SHARED lock, [db] is unable to commit the
145 # transaction. If it tries, an error is returned and the connection
146 # upgrades to a PENDING lock.
147 #
148 # Once this happens, [db] can read the database and see the new content,
149 # [db2] (still holding SHARED) can still read the old content, but [db3]
150 # (not holding any lock) is prevented by [db]'s PENDING from reading
151 # the database.
152 #
153 do_test pager1-$tn.24 { csql1 { COMMIT } } {1 {database is locked}}
154 do_test pager1-$tn-25 {
155 sql1 { PRAGMA lock_status }
156 } {main pending temp closed}
157 do_test pager1-$tn.26 { sql1 { SELECT * FROM t1 } } {11 one 12 two 13 three}
158 do_test pager1-$tn.27 { sql2 { SELECT * FROM t1 } } {1 one 2 two 3 three}
159 do_test pager1-$tn.28 { csql3 { SELECT * FROM t1 } } {1 {database is locked}}
160
161 # Have [db2] commit its read transaction, releasing the SHARED lock it
162 # is holding. Now, neither [db2] nor [db3] may read the database (as [db]
163 # is still holding a PENDING).
164 #
165 do_test pager1-$tn.29 { sql2 { COMMIT } } {}
166 do_test pager1-$tn.30 { csql2 { SELECT * FROM t1 } } {1 {database is locked}}
167 do_test pager1-$tn.31 { csql3 { SELECT * FROM t1 } } {1 {database is locked}}
168
169 # [db] is now able to commit the transaction. Once the transaction is
170 # committed, all three connections can read the new content.
171 #
172 do_test pager1-$tn.25 { sql1 { UPDATE t1 SET a = a+10 } } {}
173 do_test pager1-$tn.26 { sql1 { COMMIT } } {}
174 do_test pager1-$tn.27 { sql1 { SELECT * FROM t1 } } {21 one 22 two 23 three}
175 do_test pager1-$tn.27 { sql2 { SELECT * FROM t1 } } {21 one 22 two 23 three}
176 do_test pager1-$tn.28 { sql3 { SELECT * FROM t1 } } {21 one 22 two 23 three}
dane91a54e2010-06-15 17:44:47 +0000177}
178
dan53f04f32010-06-16 12:30:10 +0000179#-------------------------------------------------------------------------
180# Savepoint related test cases.
181#
danb0ac3e32010-06-16 10:55:42 +0000182do_test pager1-3.1 {
183 faultsim_delete_and_reopen
184 execsql {
185 CREATE TABLE t1(a PRIMARY KEY, b);
186 CREATE TABLE counter(
187 i CHECK (i<5),
188 u CHECK (u<10)
189 );
190 INSERT INTO counter VALUES(0, 0);
191 CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN
192 UPDATE counter SET i = i+1;
193 END;
194 CREATE TRIGGER tr2 AFTER UPDATE ON t1 BEGIN
195 UPDATE counter SET u = u+1;
196 END;
197 }
198 execsql { SELECT * FROM counter }
199} {0 0}
200
201do_execsql_test pager1-3.2 {
202 BEGIN;
203 INSERT INTO t1 VALUES(1, randomblob(1500));
204 INSERT INTO t1 VALUES(2, randomblob(1500));
205 INSERT INTO t1 VALUES(3, randomblob(1500));
206 SELECT * FROM counter;
207} {3 0}
208do_catchsql_test pager1-3.3 {
209 INSERT INTO t1 SELECT a+3, randomblob(1500) FROM t1
210} {1 {constraint failed}}
211do_execsql_test pager1-3.4 { SELECT * FROM counter } {3 0}
212do_execsql_test pager1-3.5 { SELECT a FROM t1 } {1 2 3}
213do_execsql_test pager1-3.6 { COMMIT } {}
214
dan53f04f32010-06-16 12:30:10 +0000215#-------------------------------------------------------------------------
216# Hot journal rollback related test cases.
217#
218# pager1.4.1.*: Test that the pager module deletes very small invalid
219# journal files.
220#
221# pager1.4.2.*: Test that if the master journal pointer at the end of a
222# hot-journal file appears to be corrupt (checksum does not
223# compute) the associated journal is rolled back (and no
224# xAccess() call to check for the presence of any master
225# journal file is made).
dande4996e2010-06-19 11:30:41 +0000226#
227# pager1.4.3.*: Test that the contents of a hot-journal are ignored if the
228# page-size or sector-size in the journal header appear to
229# be invalid (too large, too small or not a power of 2).
dan53f04f32010-06-16 12:30:10 +0000230#
231do_test pager1.4.1.1 {
232 faultsim_delete_and_reopen
233 execsql {
234 CREATE TABLE x(y, z);
235 INSERT INTO x VALUES(1, 2);
236 }
237 set fd [open test.db-journal w]
238 puts -nonewline $fd "helloworld"
239 close $fd
240 file exists test.db-journal
241} {1}
242do_test pager1.4.1.2 { execsql { SELECT * FROM x } } {1 2}
243do_test pager1.4.1.3 { file exists test.db-journal } {0}
244
245# Set up a [testvfs] to snapshot the file-system just before SQLite
246# deletes the master-journal to commit a multi-file transaction.
247#
248# In subsequent test cases, invoking [faultsim_restore_and_reopen] sets
249# up the file system to contain two databases, two hot-journal files and
250# a master-journal.
251#
252do_test pager1.4.2.1 {
253 testvfs tstvfs -default 1
254 tstvfs filter xDelete
255 tstvfs script xDeleteCallback
256 proc xDeleteCallback {method file args} {
257 set file [file tail $file]
258 if { [string match *mj* $file] } { faultsim_save }
259 }
260 faultsim_delete_and_reopen
261 db func a_string a_string
262 execsql {
263 ATTACH 'test.db2' AS aux;
264 PRAGMA journal_mode = DELETE;
265 PRAGMA main.cache_size = 10;
266 PRAGMA aux.cache_size = 10;
267 CREATE TABLE t1(a UNIQUE, b UNIQUE);
268 CREATE TABLE aux.t2(a UNIQUE, b UNIQUE);
269 INSERT INTO t1 VALUES(a_string(200), a_string(300));
270 INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1;
271 INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1;
272 INSERT INTO t2 SELECT * FROM t1;
273 BEGIN;
274 INSERT INTO t1 SELECT a_string(201), a_string(301) FROM t1;
275 INSERT INTO t1 SELECT a_string(202), a_string(302) FROM t1;
276 INSERT INTO t1 SELECT a_string(203), a_string(303) FROM t1;
277 INSERT INTO t1 SELECT a_string(204), a_string(304) FROM t1;
278 REPLACE INTO t2 SELECT * FROM t1;
279 COMMIT;
280 }
281 db close
282 tstvfs delete
283} {}
284do_test pager1.4.2.2 {
285 faultsim_restore_and_reopen
286 execsql {
287 SELECT count(*) FROM t1;
288 PRAGMA integrity_check;
289 }
290} {4 ok}
291do_test pager1.4.2.3 {
292 faultsim_restore_and_reopen
293 foreach f [glob test.db-mj*] { file delete -force $f }
294 execsql {
295 SELECT count(*) FROM t1;
296 PRAGMA integrity_check;
297 }
298} {64 ok}
299do_test pager1.4.2.4 {
300 faultsim_restore_and_reopen
301 hexio_write test.db-journal [expr [file size test.db-journal]-20] 123456
302 execsql {
303 SELECT count(*) FROM t1;
304 PRAGMA integrity_check;
305 }
306} {4 ok}
307do_test pager1.4.2.5 {
308 faultsim_restore_and_reopen
309 hexio_write test.db-journal [expr [file size test.db-journal]-20] 123456
310 foreach f [glob test.db-mj*] { file delete -force $f }
311 execsql {
312 SELECT count(*) FROM t1;
313 PRAGMA integrity_check;
314 }
315} {4 ok}
316
dande4996e2010-06-19 11:30:41 +0000317do_test pager1.4.3.1 {
318 testvfs tstvfs -default 1
319 tstvfs filter xSync
320 tstvfs script xSyncCallback
321 proc xSyncCallback {method file args} {
322 set file [file tail $file]
323 if { 0==[string match *journal $file] } { faultsim_save }
324 }
325 faultsim_delete_and_reopen
326 execsql {
327 PRAGMA journal_mode = DELETE;
328 CREATE TABLE t1(a, b);
329 INSERT INTO t1 VALUES(1, 2);
330 INSERT INTO t1 VALUES(3, 4);
331 }
332 db close
333 tstvfs delete
334} {}
335
336foreach {tn ofst value result} {
337 2 20 31 {1 2 3 4}
338 3 20 32 {1 2 3 4}
339 4 20 33 {1 2 3 4}
340 5 20 65536 {1 2 3 4}
341 6 20 131072 {1 2 3 4}
342
343 7 24 511 {1 2 3 4}
344 8 24 513 {1 2 3 4}
345 9 24 65536 {1 2 3 4}
346
347 10 32 65536 {1 2}
348} {
349 do_test pager1.4.3.$tn {
350 faultsim_restore_and_reopen
351 hexio_write test.db-journal $ofst [format %.8x $value]
352 execsql { SELECT * FROM t1 }
353 } $result
354}
355db close
356
357#-------------------------------------------------------------------------
dan146ed782010-06-19 17:26:37 +0000358# The following tests deal with multi-file commits.
dande4996e2010-06-19 11:30:41 +0000359#
dan146ed782010-06-19 17:26:37 +0000360# pager1-5.1.*: The case where a multi-file cannot be committed because
361# another connection is holding a SHARED lock on one of the
362# files. After the SHARED lock is removed, the COMMIT succeeds.
363#
364# pager1-5.2.*: Multi-file commits with journal_mode=memory.
365#
366# pager1-5.3.*: Multi-file commits with journal_mode=memory.
367#
368# pager1-5.4.*: Check that with synchronous=normal, the master-journal file
369# name is added to a journal file immediately after the last
370# journal record. But with synchronous=full, extra unused space
371# is allocated between the last journal record and the
372# master-journal file name so that the master-journal file
373# name does not lie on the same sector as the last journal file
374# record.
375#
376# pager1-5.5.*:
377#
378do_test pager1-5.1.1 {
379 faultsim_delete_and_reopen
380 execsql {
381 ATTACH 'test.db2' AS aux;
382 CREATE TABLE t1(a, b);
383 CREATE TABLE aux.t2(a, b);
384 INSERT INTO t1 VALUES(17, 'Lenin');
385 INSERT INTO t1 VALUES(22, 'Stalin');
386 INSERT INTO t1 VALUES(53, 'Khrushchev');
387 }
388} {}
389do_test pager1-5.1.2 {
390 execsql {
391 BEGIN;
392 INSERT INTO t1 VALUES(64, 'Brezhnev');
393 INSERT INTO t2 SELECT * FROM t1;
394 }
395 sqlite3 db2 test.db2
396 execsql {
397 BEGIN;
398 SELECT * FROM t2;
399 } db2
400} {}
401do_test pager1-5.1.3 {
402 catchsql COMMIT
403} {1 {database is locked}}
404do_test pager1-5.1.4 {
405 execsql COMMIT db2
406 execsql COMMIT
407 execsql { SELECT * FROM t2 } db2
408} {17 Lenin 22 Stalin 53 Khrushchev 64 Brezhnev}
409do_test pager1-5.1.5 {
410 db2 close
411} {}
412
413do_test pager1-5.2.1 {
414 execsql {
415 PRAGMA journal_mode = memory;
416 BEGIN;
417 INSERT INTO t1 VALUES(84, 'Andropov');
418 INSERT INTO t2 VALUES(84, 'Andropov');
419 COMMIT;
420 }
421} {memory}
422do_test pager1-5.3.1 {
423 execsql {
424 PRAGMA journal_mode = off;
425 BEGIN;
426 INSERT INTO t1 VALUES(85, 'Gorbachev');
427 INSERT INTO t2 VALUES(85, 'Gorbachev');
428 COMMIT;
429 }
430} {off}
431
432do_test pager1-5.4.1 {
433 db close
434 testvfs tv
435 sqlite3 db test.db -vfs tv
436 execsql { ATTACH 'test.db2' AS aux }
437
438 tv filter xDelete
439 tv script max_journal_size
440 tv sectorsize 512
441 set ::max_journal 0
442 proc max_journal_size {method args} {
443 set sz 0
444 catch { set sz [file size test.db-journal] }
445 if {$sz > $::max_journal} {
446 set ::max_journal $sz
447 }
448 return SQLITE_OK
449 }
450 execsql {
451 PRAGMA journal_mode = DELETE;
452 PRAGMA synchronous = NORMAL;
453 BEGIN;
454 INSERT INTO t1 VALUES(85, 'Gorbachev');
455 INSERT INTO t2 VALUES(85, 'Gorbachev');
456 COMMIT;
457 }
458 set ::max_journal
459} [expr 2615+[string length [pwd]]]
460do_test pager1-5.4.2 {
461 set ::max_journal 0
462 execsql {
463 PRAGMA synchronous = full;
464 BEGIN;
465 DELETE FROM t1 WHERE b = 'Lenin';
466 DELETE FROM t2 WHERE b = 'Lenin';
467 COMMIT;
468 }
469 set ::max_journal
470} [expr 3111+[string length [pwd]]]
471db close
472tv delete
473
474do_test pager1-5.5.1 {
475 sqlite3 db test.db
476 execsql {
477 ATTACH 'test.db2' AS aux;
478 PRAGMA journal_mode = PERSIST;
479 CREATE TABLE t3(a, b);
480 INSERT INTO t3 SELECT randomblob(1500), randomblob(1500) FROM t1;
481 UPDATE t3 SET b = randomblob(1500);
482 }
483 expr [file size test.db-journal] > 15000
484} {1}
485do_test pager1-5.5.2 {
486 execsql {
487 PRAGMA synchronous = full;
488 BEGIN;
489 DELETE FROM t1 WHERE b = 'Stalin';
490 DELETE FROM t2 WHERE b = 'Stalin';
491 COMMIT;
492 }
493 file size test.db-journal
494} {0}
495
496
497#-------------------------------------------------------------------------
498# The following tests work with "PRAGMA max_page_count"
499#
500do_test pager1-6.1 {
501 faultsim_delete_and_reopen
502 execsql {
503 PRAGMA max_page_count = 10;
504 CREATE TABLE t2(a, b);
505 CREATE TABLE t3(a, b);
506 CREATE TABLE t4(a, b);
507 CREATE TABLE t5(a, b);
508 CREATE TABLE t6(a, b);
509 CREATE TABLE t7(a, b);
510 CREATE TABLE t8(a, b);
511 CREATE TABLE t9(a, b);
512 CREATE TABLE t10(a, b);
513 }
514} {10}
515do_test pager1-6.2 {
516 catchsql {
517 CREATE TABLE t11(a, b);
518 }
519} {1 {database or disk is full}}
dande4996e2010-06-19 11:30:41 +0000520
dan153eda02010-06-21 07:45:47 +0000521
522#-------------------------------------------------------------------------
523# The following tests work with "PRAGMA journal_mode=TRUNCATE" and
524# "PRAGMA locking_mode=EXCLUSIVE".
525#
526# Each test is specified with 5 variables. As follows:
527#
528# $tn: Test Number. Used as part of the [do_test] test names.
529# $sql: SQL to execute.
530# $res: Expected result of executing $sql.
531# $js: The expected size of the journal file, in bytes, after executing
532# the SQL script. Or -1 if the journal is not expected to exist.
533# $ws: The expected size of the WAL file, in bytes, after executing
534# the SQL script. Or -1 if the WAL is not expected to exist.
535#
536faultsim_delete_and_reopen
537foreach {tn sql res js ws} [subst {
538
539 1 {
540 CREATE TABLE t1(a, b);
541 PRAGMA auto_vacuum=OFF;
542 PRAGMA synchronous=NORMAL;
543 PRAGMA page_size=1024;
544 PRAGMA locking_mode=EXCLUSIVE;
545 PRAGMA journal_mode=TRUNCATE;
546 INSERT INTO t1 VALUES(1, 2);
547 } {exclusive truncate} 0 -1
548
549 2 {
550 BEGIN IMMEDIATE;
551 SELECT * FROM t1;
552 COMMIT;
553 } {1 2} 0 -1
554
555 3 {
556 BEGIN;
557 SELECT * FROM t1;
558 COMMIT;
559 } {1 2} 0 -1
560
561 4 { PRAGMA journal_mode = WAL } wal -1 -1
562 5 { INSERT INTO t1 VALUES(3, 4) } {} -1 [wal_file_size 1 1024]
563 6 { PRAGMA locking_mode = NORMAL } normal -1 [wal_file_size 1 1024]
564 7 { INSERT INTO t1 VALUES(5, 6); } {} -1 [wal_file_size 2 1024]
565
566 8 { PRAGMA journal_mode = TRUNCATE } truncate 0 -1
567 9 { INSERT INTO t1 VALUES(7, 8) } {} 0 -1
568 10 { SELECT * FROM t1 } {1 2 3 4 5 6 7 8} 0 -1
569
570}] {
571 do_execsql_test pager1-7.1.$tn.1 $sql $res
572 catch { set J -1 ; set J [file size test.db-journal] }
573 catch { set W -1 ; set W [file size test.db-wal] }
574 do_test pager1-7.1.$tn.2 { list $J $W } [list $js $ws]
575}
576
577do_test pager1-8.1 {
578 faultsim_delete_and_reopen
579 db close
580 sqlite3 db :memory:
581 execsql {
582 CREATE TABLE x1(x);
583 INSERT INTO x1 VALUES('Charles');
584 INSERT INTO x1 VALUES('James');
585 INSERT INTO x1 VALUES('Mary');
586 SELECT * FROM x1;
587 }
588} {Charles James Mary}
589do_test pager1-8.2 {
590 db close
591 sqlite3 db :memory:
592 catchsql { SELECT * FROM x1 }
593} {1 {no such table: x1}}
594
dane91a54e2010-06-15 17:44:47 +0000595finish_test
596