Remove superfluous option to Tcl 'lsort' in the vtabH test file.
[sqlite.git] / test / snapshot.test
blob99d3ed47c366387e3ef46789ea0e214a20fd325f
1 # 2015 December 7
3 # The author disclaims copyright to this source code.  In place of
4 # a legal notice, here is a blessing:
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.
10 #***********************************************************************
11 # This file implements regression tests for SQLite library. The focus
12 # of this file is the sqlite3_snapshot_xxx() APIs.
15 set testdir [file dirname $argv0]
16 source $testdir/tester.tcl
17 ifcapable !snapshot {finish_test; return}
18 set testprefix snapshot
20 # This test does not work with the inmemory_journal permutation. The reason
21 # is that each connection opened as part of this permutation executes
22 # "PRAGMA journal_mode=memory", which fails if the database is in wal mode
23 # and there are one or more existing connections.
24 if {[permutation]=="inmemory_journal"} {
25   finish_test
26   return
29 foreach {tn tcl} {
30   1 {
31     proc snapshot_get {DB DBNAME} {
32       uplevel [list sqlite3_snapshot_get $DB $DBNAME]
33     }
34     proc snapshot_open {DB DBNAME SNAPSHOT} {
35       uplevel [list sqlite3_snapshot_open $DB $DBNAME $SNAPSHOT]
36     }
37     proc snapshot_free {SNAPSHOT} {
38       uplevel [list sqlite3_snapshot_free $SNAPSHOT]
39     }
40     proc snapshot_cmp {SNAPSHOT1 SNAPSHOT2} {
41       uplevel [list sqlite3_snapshot_cmp $SNAPSHOT1 $SNAPSHOT2]
42     }
43   }
45   2 {
46     proc snapshot_get {DB DBNAME} {
47       uplevel [list sqlite3_snapshot_get_blob $DB $DBNAME]
48     }
49     proc snapshot_open {DB DBNAME SNAPSHOT} {
50       uplevel [list sqlite3_snapshot_open_blob $DB $DBNAME $SNAPSHOT]
51     }
52     proc snapshot_free {SNAPSHOT} {
53     }
54     proc snapshot_cmp {SNAPSHOT1 SNAPSHOT2} {
55       uplevel [list sqlite3_snapshot_cmp_blob $SNAPSHOT1 $SNAPSHOT2]
56     }
57   }
58 } {
60   reset_db
61   eval $tcl
63   #-------------------------------------------------------------------------
64   # Check some error conditions in snapshot_get(). It is an error if:
65   #
66   #  1) snapshot_get() is called on a non-WAL database, or
67   #  2) there is an open write transaction on the database.
68   #  3) the database handle is in auto-commit mode
69   #
70   do_execsql_test $tn.1.0 {
71     CREATE TABLE t1(a, b);
72     INSERT INTO t1 VALUES(1, 2);
73     INSERT INTO t1 VALUES(3, 4);
74   }
76   do_test $tn.1.1.1 {
77     execsql { BEGIN; SELECT * FROM t1; }
78     list [catch { snapshot_get db main } msg] $msg
79   } {1 SQLITE_ERROR}
80   do_execsql_test $tn.1.1.2 COMMIT
82   do_test $tn.1.2.1 {
83     execsql {
84       PRAGMA journal_mode = WAL;
85       BEGIN;
86         INSERT INTO t1 VALUES(5, 6);
87         INSERT INTO t1 VALUES(7, 8);
88     }
89     list [catch { snapshot_get db main } msg] $msg
90   } {1 SQLITE_ERROR}
91   do_execsql_test $tn.1.2.2 COMMIT
93   do_test $tn.1.3.1 {
94     list [catch { snapshot_get db main } msg] $msg
95   } {1 SQLITE_ERROR}
96   do_test $tn.1.3.2 {
97     db trans { set snap [snapshot_get db main] }
98     snapshot_free $snap
99   } {}
101   #-------------------------------------------------------------------------
102   # Check that a simple case works. Reuse the database created by the
103   # block of tests above.
104   #
105   do_execsql_test $tn.2.1.0 {
106     BEGIN;
107       SELECT * FROM t1;
108   } {1 2 3 4 5 6 7 8}
110   do_test $tn.2.1.1 {
111     set snapshot [snapshot_get db main]
112     execsql {
113       COMMIT;
114       INSERT INTO t1 VALUES(9, 10);
115       SELECT * FROM t1;
116     }
117   } {1 2 3 4 5 6 7 8 9 10}
119   do_test $tn.2.1.2 {
120     execsql BEGIN
121     snapshot_open db main $snapshot
122     execsql {
123       SELECT * FROM t1;
124     }
125   } {1 2 3 4 5 6 7 8}
127   do_test $tn.2.1.3 {
128     snapshot_free $snapshot
129     execsql COMMIT
130   } {}
132   do_test $tn.2.2.0 {
133     sqlite3 db2 test.db
134     execsql {
135       BEGIN;
136         SELECT * FROM t1;
137     } db2
138   } {1 2 3 4 5 6 7 8 9 10}
140   do_test $tn.2.2.1 {
141     set snapshot [snapshot_get db2 main]
142     execsql {
143       INSERT INTO t1 VALUES(11, 12);
144       SELECT * FROM t1;
145     }
146   } {1 2 3 4 5 6 7 8 9 10 11 12}
148   do_test $tn.2.2.2 {
149     execsql BEGIN
150     snapshot_open db main $snapshot
151     execsql {
152       SELECT * FROM t1;
153     }
154   } {1 2 3 4 5 6 7 8 9 10}
156   do_test $tn.2.2.3 {
157     snapshot_free $snapshot
158     execsql COMMIT
159     execsql COMMIT db2
160     db2 close
161   } {}
163   do_test $tn.2.3.1 {
164     execsql { DELETE FROM t1 WHERE a>6 }
165     db trans { set snapshot [snapshot_get db main] }
166     execsql {
167       INSERT INTO t1 VALUES('a', 'b');
168       INSERT INTO t1 VALUES('c', 'd');
169       SELECT * FROM t1;
170     }
171   } {1 2 3 4 5 6 a b c d}
172   do_test $tn.2.3.2 {
173     execsql BEGIN
174     snapshot_open db main $snapshot
175     execsql { SELECT * FROM t1 }
176   } {1 2 3 4 5 6}
178   do_test $tn.2.3.3 {
179     catchsql {
180       INSERT INTO t1 VALUES('x','y')
181     }
182   } {1 {database is locked}}
183   do_test $tn.2.3.4 {
184     execsql COMMIT
185     snapshot_free $snapshot
186   } {}
188   #-------------------------------------------------------------------------
189   # Check some errors in snapshot_open(). It is an error if:
190   #
191   #   1) the db is in auto-commit mode,
192   #   2) the db has an open (read or write) transaction,
193   #   3) the db is not a wal database,
194   #
195   # Reuse the database created by earlier tests.
196   #
197   do_execsql_test $tn.3.0.0 {
198     CREATE TABLE t2(x, y);
199     INSERT INTO t2 VALUES('a', 'b');
200     INSERT INTO t2 VALUES('c', 'd');
201     BEGIN;
202       SELECT * FROM t2;
203   } {a b c d}
204   do_test $tn.3.0.1 {
205     set snapshot [snapshot_get db main]
206     execsql { COMMIT }
207     execsql { INSERT INTO t2 VALUES('e', 'f'); }
208   } {}
210   do_test $tn.3.1 {
211     list [catch {snapshot_open db main $snapshot } msg] $msg
212   } {1 SQLITE_ERROR}
214   do_test $tn.3.2.1 {
215     execsql {
216       BEGIN;
217         SELECT * FROM t2;
218     }
219   } {a b c d e f}
220   do_test $tn.3.2.2 {
221     list [catch {snapshot_open db main $snapshot } msg] $msg
222   } {1 SQLITE_ERROR}
224   do_test $tn.3.2.3 {
225     execsql {
226       COMMIT;
227       BEGIN;
228         INSERT INTO t2 VALUES('g', 'h');
229     }
230     list [catch {snapshot_open db main $snapshot } msg] $msg
231   } {1 SQLITE_ERROR}
232   do_execsql_test $tn.3.2.4 COMMIT
234   do_test $tn.3.3.1 {
235     execsql { PRAGMA journal_mode = DELETE }
236     execsql { BEGIN }
237     list [catch {snapshot_open db main $snapshot } msg] $msg
238   } {1 SQLITE_ERROR}
240   do_test $tn.$tn.3.3.2 {
241     snapshot_free $snapshot
242     execsql COMMIT
243   } {}
245   #-------------------------------------------------------------------------
246   # Check that SQLITE_BUSY_SNAPSHOT is returned if the specified snapshot
247   # no longer exists because the wal file has been checkpointed.
248   #
249   #   1. Reading a snapshot from the middle of a wal file is not possible
250   #      after the wal file has been checkpointed.
251   #
252   #   2. That a snapshot from the end of a wal file can not be read once
253   #      the wal file has been wrapped.
254   #
255   do_execsql_test $tn.4.1.0 {
256     PRAGMA journal_mode = wal;
257     CREATE TABLE t3(i, j);
258     INSERT INTO t3 VALUES('o', 't');
259     INSERT INTO t3 VALUES('t', 'f');
260     BEGIN;
261       SELECT * FROM t3;
262   } {wal o t t f}
264   do_test $tn.4.1.1 {
265     set snapshot [snapshot_get db main]
266     execsql COMMIT
267   } {}
268   do_test $tn.4.1.2 {
269     execsql { 
270       INSERT INTO t3 VALUES('f', 's'); 
271       BEGIN;
272     }
273     snapshot_open db main $snapshot
274     execsql { SELECT * FROM t3 }
275   } {o t t f}
277   do_test $tn.4.1.3 {
278     execsql { 
279       COMMIT;
280       PRAGMA wal_checkpoint;
281       BEGIN;
282     }
283     list [catch {snapshot_open db main $snapshot} msg] $msg
284   } {1 SQLITE_BUSY_SNAPSHOT}
285   do_test $tn.4.1.4 {
286     snapshot_free $snapshot
287     execsql COMMIT
288   } {}
290   do_test $tn.4.2.1 {
291     execsql {
292       INSERT INTO t3 VALUES('s', 'e');
293       INSERT INTO t3 VALUES('n', 't');
294       BEGIN;
295         SELECT * FROM t3;
296     }
297   } {o t t f f s s e n t}
298   do_test $tn.4.2.2 {
299     set snapshot [snapshot_get db main]
300     execsql {
301       COMMIT;
302       PRAGMA wal_checkpoint;
303       BEGIN;
304     }
305     snapshot_open db main $snapshot
306     execsql { SELECT * FROM t3 }
307   } {o t t f f s s e n t}
308   do_test $tn.4.2.3 {
309     execsql {
310       COMMIT;
311       INSERT INTO t3 VALUES('e', 't');
312       BEGIN;
313     }
314     list [catch {snapshot_open db main $snapshot} msg] $msg
315   } {1 SQLITE_BUSY_SNAPSHOT}
316   do_test $tn.4.2.4 {
317     snapshot_free $snapshot
318   } {}
320   #-------------------------------------------------------------------------
321   # Check that SQLITE_BUSY is returned if a checkpoint is running when
322   # sqlite3_snapshot_open() is called.
323   #
324   reset_db
325   db close
326   testvfs tvfs
327   sqlite3 db test.db -vfs tvfs
329   do_execsql_test $tn.5.1 {
330     PRAGMA journal_mode = wal;
331     CREATE TABLE x1(x, xx, xxx);
332     INSERT INTO x1 VALUES('z', 'zz', 'zzz');
333     BEGIN;
334       SELECT * FROM x1;
335   } {wal z zz zzz}
337   do_test $tn.5.2 {
338     set ::snapshot [snapshot_get db main]
339     sqlite3 db2 test.db -vfs tvfs
340     execsql {
341       INSERT INTO x1 VALUES('a', 'aa', 'aaa');
342       COMMIT;
343     }
344   } {}
346   set t53 0
347   proc write_callback {args} {
348     do_test $tn.5.3.[incr ::t53] {
349       execsql BEGIN
350       list [catch { snapshot_open db main $::snapshot } msg] $msg
351     } {1 SQLITE_BUSY}
352     catchsql COMMIT
353   }
355   tvfs filter xWrite
356   tvfs script write_callback
357   db2 eval { PRAGMA wal_checkpoint }
358   db close
359   db2 close
360   tvfs delete
361   snapshot_free $snapshot
363   #-------------------------------------------------------------------------
364   # Test that sqlite3_snapshot_get() may be called immediately after
365   # "BEGIN; PRAGMA user_version;". And that sqlite3_snapshot_open() may
366   # be called after opening the db handle and running the script
367   # "PRAGMA user_version; BEGIN".
368   reset_db
369   do_execsql_test $tn.6.1 {
370     PRAGMA journal_mode = wal;
371     CREATE TABLE x1(x, xx, xxx);
372     INSERT INTO x1 VALUES('z', 'zz', 'zzz');
373     BEGIN;
374       PRAGMA user_version;
375   } {wal 0}
376   do_test $tn.6.2 {
377     set ::snapshot [snapshot_get db main]
378     execsql {
379       INSERT INTO x1 VALUES('a', 'aa', 'aaa');
380       COMMIT;
381     }
382   } {}
383   do_test $tn.6.3 {
384     sqlite3 db2 test.db 
385     db2 eval "PRAGMA user_version ; BEGIN"
386     snapshot_open db2 main $::snapshot
387     db2 eval { SELECT * FROM x1 }
388   } {z zz zzz}
389   do_test $tn.6.4 {
390     db2 close
391     sqlite3 db2 test.db 
392     db2 eval "PRAGMA application_id"
393     db2 eval "BEGIN"
394     snapshot_open db2 main $::snapshot
395     db2 eval { SELECT * FROM x1 }
396   } {z zz zzz}
398   do_test $tn.6.5 {
399     db2 close
400     sqlite3 db2 test.db 
401     db2 eval "BEGIN"
402     list [catch {snapshot_open db2 main $::snapshot} msg] $msg
403   } {1 SQLITE_ERROR}
405   snapshot_free $snapshot
407   #-------------------------------------------------------------------------
408   # The following tests investigate the sqlite3_snapshot_cmp() API.
409   #
411   # Compare snapshots $p1 and $p2, checking that the result is $r.
412   #
413   proc do_snapshot_cmp_test {tn p1 p2 r} {
414     uplevel [list do_test $tn.1 [list snapshot_cmp $p1 $p2] $r]
415     uplevel [list do_test $tn.2 [list snapshot_cmp $p2 $p1] [expr $r*-1]]
416     uplevel [list do_test $tn.3 [list snapshot_cmp $p1 $p1] 0]
417     uplevel [list do_test $tn.4 [list snapshot_cmp $p2 $p2] 0]
418   }
420   catch { db2 close }
421   reset_db
423   do_execsql_test $tn.7.1 {
424     PRAGMA journal_mode = wal;
425     CREATE TABLE t1(x);
426   } wal
428   do_test $tn.7.1.2 {
429     execsql { BEGIN ; PRAGMA application_id }
430     set p1 [snapshot_get db main]
431     execsql {
432       INSERT INTO t1 VALUES(10);
433       COMMIT;
434     }
435     execsql { BEGIN ; PRAGMA application_id }
436     set p2 [snapshot_get db main]
437     execsql COMMIT
438   } {}
440   do_snapshot_cmp_test $tn.7.1.3 $p1 $p2 -1
441   snapshot_free $p1
442   snapshot_free $p2
444   do_execsql_test $tn.7.2.1 {
445     INSERT INTO t1 VALUES(11);
446     INSERT INTO t1 VALUES(12);
447     INSERT INTO t1 VALUES(13);
448     BEGIN; 
449       PRAGMA application_id;
450   } {0}
451   do_test $tn.7.2.2 {
452     set p1 [snapshot_get db main]
453     execsql {
454       COMMIT;
455       INSERT INTO t1 VALUES(14);
456       PRAGMA wal_checkpoint;
457       BEGIN;
458         PRAGMA application_id;
459     }
460     set p2 [snapshot_get db main]
461     execsql COMMIT
462   } {}
464   do_snapshot_cmp_test $tn.7.2.3 $p1 $p2 -1
465   snapshot_free $p2
467   do_test $tn.7.3.1 {
468     execsql {
469       INSERT INTO t1 VALUES(14);
470       BEGIN;
471         PRAGMA application_id;
472     }
473     set p2 [snapshot_get db main]
474     execsql COMMIT
475   } {}
477   do_snapshot_cmp_test $tn.7.3.2 $p1 $p2 -1
478   snapshot_free $p1
479   snapshot_free $p2
482 finish_test