xref: /sqlite-3.40.0/test/snapshot.test (revision dfe4e6bb)
1# 2015 December 7
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 focus
12# of this file is the sqlite3_snapshot_xxx() APIs.
13#
14
15set testdir [file dirname $argv0]
16source $testdir/tester.tcl
17ifcapable !snapshot {finish_test; return}
18set testprefix snapshot
19
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.
24if {[permutation]=="inmemory_journal"} {
25  finish_test
26  return
27}
28
29#-------------------------------------------------------------------------
30# Check some error conditions in snapshot_get(). It is an error if:
31#
32#  1) snapshot_get() is called on a non-WAL database, or
33#  2) there is an open write transaction on the database.
34#
35do_execsql_test 1.0 {
36  CREATE TABLE t1(a, b);
37  INSERT INTO t1 VALUES(1, 2);
38  INSERT INTO t1 VALUES(3, 4);
39}
40
41do_test 1.1.1 {
42  execsql { BEGIN; SELECT * FROM t1; }
43  list [catch { sqlite3_snapshot_get db main } msg] $msg
44} {1 SQLITE_ERROR}
45do_execsql_test 1.1.2 COMMIT
46
47do_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}
56do_execsql_test 1.3.2 COMMIT
57
58#-------------------------------------------------------------------------
59# Check that a simple case works. Reuse the database created by the
60# block of tests above.
61#
62do_execsql_test 2.1.0 {
63  BEGIN;
64    SELECT * FROM t1;
65} {1 2 3 4 5 6 7 8}
66
67do_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}
75
76do_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}
83
84do_test 2.1.3 {
85  sqlite3_snapshot_free $snapshot
86  execsql COMMIT
87} {}
88
89do_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}
96
97do_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}
104
105do_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}
112
113do_test 2.2.3 {
114  sqlite3_snapshot_free $snapshot
115  execsql COMMIT
116  execsql COMMIT db2
117  db2 close
118} {}
119
120do_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}
129do_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}
134
135do_test 2.3.3 {
136  catchsql {
137    INSERT INTO t1 VALUES('x','y')
138  }
139} {1 {database is locked}}
140do_test 2.3.4 {
141  execsql COMMIT
142  sqlite3_snapshot_free $snapshot
143} {}
144
145#-------------------------------------------------------------------------
146# Check some errors in sqlite3_snapshot_open(). It is an error if:
147#
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,
151#
152# Reuse the database created by earlier tests.
153#
154do_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}
161do_test 3.0.1 {
162  set snapshot [sqlite3_snapshot_get db main]
163  execsql { COMMIT }
164  execsql { INSERT INTO t2 VALUES('e', 'f'); }
165} {}
166
167do_test 3.1 {
168  list [catch {sqlite3_snapshot_open db main $snapshot } msg] $msg
169} {1 SQLITE_ERROR}
170
171do_test 3.2.1 {
172  execsql {
173    BEGIN;
174      SELECT * FROM t2;
175  }
176} {a b c d e f}
177do_test 3.2.2 {
178  list [catch {sqlite3_snapshot_open db main $snapshot } msg] $msg
179} {1 SQLITE_ERROR}
180
181do_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}
189do_execsql_test 3.2.4 COMMIT
190
191do_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}
196
197do_test 3.3.2 {
198  sqlite3_snapshot_free $snapshot
199  execsql COMMIT
200} {}
201
202#-------------------------------------------------------------------------
203# Check that SQLITE_BUSY_SNAPSHOT is returned if the specified snapshot
204# no longer exists because the wal file has been checkpointed.
205#
206#   1. Reading a snapshot from the middle of a wal file is not possible
207#      after the wal file has been checkpointed.
208#
209#   2. That a snapshot from the end of a wal file can not be read once
210#      the wal file has been wrapped.
211#
212do_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}
220
221do_test 4.1.1 {
222  set snapshot [sqlite3_snapshot_get db main]
223  execsql COMMIT
224} {}
225do_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}
233
234do_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}
242do_test 4.1.4 {
243  sqlite3_snapshot_free $snapshot
244  execsql COMMIT
245} {}
246
247do_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}
255do_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}
265do_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}
273do_test 4.2.4 {
274  sqlite3_snapshot_free $snapshot
275} {}
276
277#-------------------------------------------------------------------------
278# Check that SQLITE_BUSY is returned if a checkpoint is running when
279# sqlite3_snapshot_open() is called.
280#
281reset_db
282db close
283testvfs tvfs
284sqlite3 db test.db -vfs tvfs
285
286do_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}
293
294do_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} {}
302
303set t53 0
304proc 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
310}
311
312tvfs filter xWrite
313tvfs script write_callback
314db2 eval { PRAGMA wal_checkpoint }
315db close
316db2 close
317tvfs delete
318sqlite3_snapshot_free $snapshot
319
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".
325reset_db
326do_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}
333do_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} {}
340do_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}
346do_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}
354
355do_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}
361
362sqlite3_snapshot_free $snapshot
363
364#-------------------------------------------------------------------------
365# The following tests investigate the sqlite3_snapshot_cmp() API.
366#
367
368# Compare snapshots $p1 and $p2, checking that the result is $r.
369#
370proc 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]
375}
376
377catch { db2 close }
378reset_db
379
380do_execsql_test 7.1 {
381  PRAGMA journal_mode = wal;
382  CREATE TABLE t1(x);
383} wal
384
385do_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} {}
396
397do_snapshot_cmp_test 7.1.3 $p1 $p2 -1
398sqlite3_snapshot_free $p1
399sqlite3_snapshot_free $p2
400
401do_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}
408do_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} {}
420
421do_snapshot_cmp_test 7.2.3 $p1 $p2 -1
422sqlite3_snapshot_free $p2
423
424do_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} {}
433
434do_snapshot_cmp_test 7.3.2 $p1 $p2 -1
435sqlite3_snapshot_free $p1
436sqlite3_snapshot_free $p2
437
438finish_test
439