Merge updates from trunk.
[sqlite.git] / test / snapshot.test
blobb2d61941a716ca00a61ef21171b8cb3b0a66b8d0
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 #-------------------------------------------------------------------------
30 # Check some error conditions in snapshot_get(). It is an error if:
32 #  1) snapshot_get() is called on a non-WAL database, or
33 #  2) there is an open write transaction on the database.
35 do_execsql_test 1.0 {
36   CREATE TABLE t1(a, b);
37   INSERT INTO t1 VALUES(1, 2);
38   INSERT INTO t1 VALUES(3, 4);
41 do_test 1.1.1 {
42   execsql { BEGIN; SELECT * FROM t1; }
43   list [catch { sqlite3_snapshot_get db main } msg] $msg
44 } {1 SQLITE_ERROR}
45 do_execsql_test 1.1.2 COMMIT
47 do_test 1.2.1 {
48   execsql {
49     PRAGMA journal_mode = WAL;
50     BEGIN;
51       INSERT INTO t1 VALUES(5, 6);
52       INSERT INTO t1 VALUES(7, 8);
53   }
54   list [catch { sqlite3_snapshot_get db main } msg] $msg
55 } {1 SQLITE_ERROR}
56 do_execsql_test 1.3.2 COMMIT
58 #-------------------------------------------------------------------------
59 # Check that a simple case works. Reuse the database created by the
60 # block of tests above.
62 do_execsql_test 2.1.0 {
63   BEGIN;
64     SELECT * FROM t1;
65 } {1 2 3 4 5 6 7 8}
67 do_test 2.1.1 {
68   set snapshot [sqlite3_snapshot_get db main]
69   execsql {
70     COMMIT;
71     INSERT INTO t1 VALUES(9, 10);
72     SELECT * FROM t1;
73   }
74 } {1 2 3 4 5 6 7 8 9 10}
76 do_test 2.1.2 {
77   execsql BEGIN
78   sqlite3_snapshot_open db main $snapshot
79   execsql {
80     SELECT * FROM t1;
81   }
82 } {1 2 3 4 5 6 7 8}
84 do_test 2.1.3 {
85   sqlite3_snapshot_free $snapshot
86   execsql COMMIT
87 } {}
89 do_test 2.2.0 {
90   sqlite3 db2 test.db
91   execsql {
92     BEGIN;
93       SELECT * FROM t1;
94   } db2
95 } {1 2 3 4 5 6 7 8 9 10}
97 do_test 2.2.1 {
98   set snapshot [sqlite3_snapshot_get db2 main]
99   execsql {
100     INSERT INTO t1 VALUES(11, 12);
101     SELECT * FROM t1;
102   }
103 } {1 2 3 4 5 6 7 8 9 10 11 12}
105 do_test 2.2.2 {
106   execsql BEGIN
107   sqlite3_snapshot_open db main $snapshot
108   execsql {
109     SELECT * FROM t1;
110   }
111 } {1 2 3 4 5 6 7 8 9 10}
113 do_test 2.2.3 {
114   sqlite3_snapshot_free $snapshot
115   execsql COMMIT
116   execsql COMMIT db2
117   db2 close
118 } {}
120 do_test 2.3.1 {
121   execsql { DELETE FROM t1 WHERE a>6 }
122   set snapshot [sqlite3_snapshot_get db main]
123   execsql {
124     INSERT INTO t1 VALUES('a', 'b');
125     INSERT INTO t1 VALUES('c', 'd');
126     SELECT * FROM t1;
127   }
128 } {1 2 3 4 5 6 a b c d}
129 do_test 2.3.2 {
130   execsql BEGIN
131   sqlite3_snapshot_open db main $snapshot
132   execsql { SELECT * FROM t1 }
133 } {1 2 3 4 5 6}
135 do_test 2.3.3 {
136   catchsql {
137     INSERT INTO t1 VALUES('x','y')
138   }
139 } {1 {database is locked}}
140 do_test 2.3.4 {
141   execsql COMMIT
142   sqlite3_snapshot_free $snapshot
143 } {}
145 #-------------------------------------------------------------------------
146 # Check some errors in sqlite3_snapshot_open(). It is an error if:
148 #   1) the db is in auto-commit mode,
149 #   2) the db has an open (read or write) transaction,
150 #   3) the db is not a wal database,
152 # Reuse the database created by earlier tests.
154 do_execsql_test 3.0.0 {
155   CREATE TABLE t2(x, y);
156   INSERT INTO t2 VALUES('a', 'b');
157   INSERT INTO t2 VALUES('c', 'd');
158   BEGIN;
159     SELECT * FROM t2;
160 } {a b c d}
161 do_test 3.0.1 {
162   set snapshot [sqlite3_snapshot_get db main]
163   execsql { COMMIT }
164   execsql { INSERT INTO t2 VALUES('e', 'f'); }
165 } {}
167 do_test 3.1 {
168   list [catch {sqlite3_snapshot_open db main $snapshot } msg] $msg
169 } {1 SQLITE_ERROR}
171 do_test 3.2.1 {
172   execsql {
173     BEGIN;
174       SELECT * FROM t2;
175   }
176 } {a b c d e f}
177 do_test 3.2.2 {
178   list [catch {sqlite3_snapshot_open db main $snapshot } msg] $msg
179 } {1 SQLITE_ERROR}
181 do_test 3.2.3 {
182   execsql {
183     COMMIT;
184     BEGIN;
185       INSERT INTO t2 VALUES('g', 'h');
186   }
187   list [catch {sqlite3_snapshot_open db main $snapshot } msg] $msg
188 } {1 SQLITE_ERROR}
189 do_execsql_test 3.2.4 COMMIT
191 do_test 3.3.1 {
192   execsql { PRAGMA journal_mode = DELETE }
193   execsql { BEGIN }
194   list [catch {sqlite3_snapshot_open db main $snapshot } msg] $msg
195 } {1 SQLITE_ERROR}
197 do_test 3.3.2 {
198   sqlite3_snapshot_free $snapshot
199   execsql COMMIT
200 } {}
202 #-------------------------------------------------------------------------
203 # Check that SQLITE_BUSY_SNAPSHOT is returned if the specified snapshot
204 # no longer exists because the wal file has been checkpointed.
206 #   1. Reading a snapshot from the middle of a wal file is not possible
207 #      after the wal file has been checkpointed.
209 #   2. That a snapshot from the end of a wal file can not be read once
210 #      the wal file has been wrapped.
212 do_execsql_test 4.1.0 {
213   PRAGMA journal_mode = wal;
214   CREATE TABLE t3(i, j);
215   INSERT INTO t3 VALUES('o', 't');
216   INSERT INTO t3 VALUES('t', 'f');
217   BEGIN;
218     SELECT * FROM t3;
219 } {wal o t t f}
221 do_test 4.1.1 {
222   set snapshot [sqlite3_snapshot_get db main]
223   execsql COMMIT
224 } {}
225 do_test 4.1.2 {
226   execsql { 
227     INSERT INTO t3 VALUES('f', 's'); 
228     BEGIN;
229   }
230   sqlite3_snapshot_open db main $snapshot
231   execsql { SELECT * FROM t3 }
232 } {o t t f}
234 do_test 4.1.3 {
235   execsql { 
236     COMMIT;
237     PRAGMA wal_checkpoint;
238     BEGIN;
239   }
240   list [catch {sqlite3_snapshot_open db main $snapshot} msg] $msg
241 } {1 SQLITE_BUSY_SNAPSHOT}
242 do_test 4.1.4 {
243   sqlite3_snapshot_free $snapshot
244   execsql COMMIT
245 } {}
247 do_test 4.2.1 {
248   execsql {
249     INSERT INTO t3 VALUES('s', 'e');
250     INSERT INTO t3 VALUES('n', 't');
251     BEGIN;
252       SELECT * FROM t3;
253   }
254 } {o t t f f s s e n t}
255 do_test 4.2.2 {
256   set snapshot [sqlite3_snapshot_get db main]
257   execsql {
258     COMMIT;
259     PRAGMA wal_checkpoint;
260     BEGIN;
261   }
262   sqlite3_snapshot_open db main $snapshot
263   execsql { SELECT * FROM t3 }
264 } {o t t f f s s e n t}
265 do_test 4.2.3 {
266   execsql {
267     COMMIT;
268     INSERT INTO t3 VALUES('e', 't');
269     BEGIN;
270   }
271   list [catch {sqlite3_snapshot_open db main $snapshot} msg] $msg
272 } {1 SQLITE_BUSY_SNAPSHOT}
273 do_test 4.2.4 {
274   sqlite3_snapshot_free $snapshot
275 } {}
277 #-------------------------------------------------------------------------
278 # Check that SQLITE_BUSY is returned if a checkpoint is running when
279 # sqlite3_snapshot_open() is called.
281 reset_db
282 db close
283 testvfs tvfs
284 sqlite3 db test.db -vfs tvfs
286 do_execsql_test 5.1 {
287   PRAGMA journal_mode = wal;
288   CREATE TABLE x1(x, xx, xxx);
289   INSERT INTO x1 VALUES('z', 'zz', 'zzz');
290   BEGIN;
291     SELECT * FROM x1;
292 } {wal z zz zzz}
294 do_test 5.2 {
295   set ::snapshot [sqlite3_snapshot_get db main]
296   sqlite3 db2 test.db -vfs tvfs
297   execsql {
298     INSERT INTO x1 VALUES('a', 'aa', 'aaa');
299     COMMIT;
300   }
301 } {}
303 set t53 0
304 proc write_callback {args} {
305   do_test 5.3.[incr ::t53] {
306     execsql BEGIN
307     list [catch { sqlite3_snapshot_open db main $::snapshot } msg] $msg
308   } {1 SQLITE_BUSY}
309   catchsql COMMIT
312 tvfs filter xWrite
313 tvfs script write_callback
314 db2 eval { PRAGMA wal_checkpoint }
315 db close
316 db2 close
317 tvfs delete
318 sqlite3_snapshot_free $snapshot
320 #-------------------------------------------------------------------------
321 # Test that sqlite3_snapshot_get() may be called immediately after
322 # "BEGIN; PRAGMA user_version;". And that sqlite3_snapshot_open() may
323 # be called after opening the db handle and running the script
324 # "PRAGMA user_version; BEGIN".
325 reset_db
326 do_execsql_test 6.1 {
327   PRAGMA journal_mode = wal;
328   CREATE TABLE x1(x, xx, xxx);
329   INSERT INTO x1 VALUES('z', 'zz', 'zzz');
330   BEGIN;
331     PRAGMA user_version;
332 } {wal 0}
333 do_test 6.2 {
334   set ::snapshot [sqlite3_snapshot_get db main]
335   execsql {
336     INSERT INTO x1 VALUES('a', 'aa', 'aaa');
337     COMMIT;
338   }
339 } {}
340 do_test 6.3 {
341   sqlite3 db2 test.db 
342   db2 eval "PRAGMA user_version ; BEGIN"
343   sqlite3_snapshot_open db2 main $::snapshot
344   db2 eval { SELECT * FROM x1 }
345 } {z zz zzz}
346 do_test 6.4 {
347   db2 close
348   sqlite3 db2 test.db 
349   db2 eval "PRAGMA application_id"
350   db2 eval "BEGIN"
351   sqlite3_snapshot_open db2 main $::snapshot
352   db2 eval { SELECT * FROM x1 }
353 } {z zz zzz}
355 do_test 6.5 {
356   db2 close
357   sqlite3 db2 test.db 
358   db2 eval "BEGIN"
359   list [catch {sqlite3_snapshot_open db2 main $::snapshot} msg] $msg
360 } {1 SQLITE_ERROR}
362 sqlite3_snapshot_free $snapshot
364 #-------------------------------------------------------------------------
365 # The following tests investigate the sqlite3_snapshot_cmp() API.
368 # Compare snapshots $p1 and $p2, checking that the result is $r.
370 proc do_snapshot_cmp_test {tn p1 p2 r} {
371   uplevel [list do_test $tn.1 [list sqlite3_snapshot_cmp $p1 $p2] $r]
372   uplevel [list do_test $tn.2 [list sqlite3_snapshot_cmp $p2 $p1] [expr $r*-1]]
373   uplevel [list do_test $tn.3 [list sqlite3_snapshot_cmp $p1 $p1] 0]
374   uplevel [list do_test $tn.4 [list sqlite3_snapshot_cmp $p2 $p2] 0]
377 catch { db2 close }
378 reset_db
380 do_execsql_test 7.1 {
381   PRAGMA journal_mode = wal;
382   CREATE TABLE t1(x);
383 } wal
385 do_test 7.1.2 {
386   execsql { BEGIN ; PRAGMA application_id }
387   set p1 [sqlite3_snapshot_get db main]
388   execsql {
389     INSERT INTO t1 VALUES(10);
390     COMMIT;
391   }
392   execsql { BEGIN ; PRAGMA application_id }
393   set p2 [sqlite3_snapshot_get db main]
394   execsql COMMIT
395 } {}
397 do_snapshot_cmp_test 7.1.3 $p1 $p2 -1
398 sqlite3_snapshot_free $p1
399 sqlite3_snapshot_free $p2
401 do_execsql_test 7.2.1 {
402   INSERT INTO t1 VALUES(11);
403   INSERT INTO t1 VALUES(12);
404   INSERT INTO t1 VALUES(13);
405   BEGIN; 
406     PRAGMA application_id;
407 } {0}
408 do_test 7.2.2 {
409   set p1 [sqlite3_snapshot_get db main]
410   execsql {
411     COMMIT;
412     INSERT INTO t1 VALUES(14);
413     PRAGMA wal_checkpoint;
414     BEGIN;
415       PRAGMA application_id;
416   }
417   set p2 [sqlite3_snapshot_get db main]
418   execsql COMMIT
419 } {}
421 do_snapshot_cmp_test 7.2.3 $p1 $p2 -1
422 sqlite3_snapshot_free $p2
424 do_test 7.3.1 {
425   execsql {
426     INSERT INTO t1 VALUES(14);
427     BEGIN;
428       PRAGMA application_id;
429   }
430   set p2 [sqlite3_snapshot_get db main]
431   execsql COMMIT
432 } {}
434 do_snapshot_cmp_test 7.3.2 $p1 $p2 -1
435 sqlite3_snapshot_free $p1
436 sqlite3_snapshot_free $p2
438 finish_test