xref: /sqlite-3.40.0/test/snapshot.test (revision 38d69855)
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#-------------------------------------------------------------------------
21# Check some error conditions in snapshot_get(). It is an error if:
22#
23#  1) snapshot_get() is called on a non-WAL database, or
24#  2) there is an open write transaction on the database.
25#
26do_execsql_test 1.0 {
27  CREATE TABLE t1(a, b);
28  INSERT INTO t1 VALUES(1, 2);
29  INSERT INTO t1 VALUES(3, 4);
30}
31
32do_test 1.1.1 {
33  execsql { BEGIN; SELECT * FROM t1; }
34  list [catch { sqlite3_snapshot_get db main } msg] $msg
35} {1 SQLITE_ERROR}
36do_execsql_test 1.1.2 COMMIT
37
38do_test 1.2.1 {
39  execsql {
40    PRAGMA journal_mode = WAL;
41    BEGIN;
42      INSERT INTO t1 VALUES(5, 6);
43      INSERT INTO t1 VALUES(7, 8);
44  }
45  list [catch { sqlite3_snapshot_get db main } msg] $msg
46} {1 SQLITE_ERROR}
47do_execsql_test 1.3.2 COMMIT
48
49#-------------------------------------------------------------------------
50# Check that a simple case works. Reuse the database created by the
51# block of tests above.
52#
53do_execsql_test 2.1.0 {
54  BEGIN;
55    SELECT * FROM t1;
56} {1 2 3 4 5 6 7 8}
57
58do_test 2.1.1 {
59  set snapshot [sqlite3_snapshot_get db main]
60  execsql {
61    COMMIT;
62    INSERT INTO t1 VALUES(9, 10);
63    SELECT * FROM t1;
64  }
65} {1 2 3 4 5 6 7 8 9 10}
66
67do_test 2.1.2 {
68  execsql BEGIN
69  sqlite3_snapshot_open db main $snapshot
70  execsql {
71    SELECT * FROM t1;
72  }
73} {1 2 3 4 5 6 7 8}
74
75do_test 2.1.3 {
76  sqlite3_snapshot_free $snapshot
77  execsql COMMIT
78} {}
79
80do_test 2.2.0 {
81  sqlite3 db2 test.db
82  execsql {
83    BEGIN;
84      SELECT * FROM t1;
85  } db2
86} {1 2 3 4 5 6 7 8 9 10}
87
88do_test 2.2.1 {
89  set snapshot [sqlite3_snapshot_get db2 main]
90  execsql {
91    INSERT INTO t1 VALUES(11, 12);
92    SELECT * FROM t1;
93  }
94} {1 2 3 4 5 6 7 8 9 10 11 12}
95
96do_test 2.2.2 {
97  execsql BEGIN
98  sqlite3_snapshot_open db main $snapshot
99  execsql {
100    SELECT * FROM t1;
101  }
102} {1 2 3 4 5 6 7 8 9 10}
103
104do_test 2.2.3 {
105  sqlite3_snapshot_free $snapshot
106  execsql COMMIT
107  execsql COMMIT db2
108  db2 close
109} {}
110
111do_test 2.3.1 {
112  execsql { DELETE FROM t1 WHERE a>6 }
113  set snapshot [sqlite3_snapshot_get db main]
114  execsql {
115    INSERT INTO t1 VALUES('a', 'b');
116    INSERT INTO t1 VALUES('c', 'd');
117    SELECT * FROM t1;
118  }
119} {1 2 3 4 5 6 a b c d}
120do_test 2.3.2 {
121  execsql BEGIN
122  sqlite3_snapshot_open db main $snapshot
123  execsql { SELECT * FROM t1 }
124} {1 2 3 4 5 6}
125
126do_test 2.3.3 {
127  catchsql {
128    INSERT INTO t1 VALUES('x','y')
129  }
130} {1 {database is locked}}
131do_test 2.3.4 {
132  execsql COMMIT
133  sqlite3_snapshot_free $snapshot
134} {}
135
136#-------------------------------------------------------------------------
137# Check some errors in sqlite3_snapshot_open(). It is an error if:
138#
139#   1) the db is in auto-commit mode,
140#   2) the db has an open (read or write) transaction,
141#   3) the db is not a wal database,
142#
143# Reuse the database created by earlier tests.
144#
145do_execsql_test 3.0.0 {
146  CREATE TABLE t2(x, y);
147  INSERT INTO t2 VALUES('a', 'b');
148  INSERT INTO t2 VALUES('c', 'd');
149  BEGIN;
150    SELECT * FROM t2;
151} {a b c d}
152do_test 3.0.1 {
153  set snapshot [sqlite3_snapshot_get db main]
154  execsql { COMMIT }
155  execsql { INSERT INTO t2 VALUES('e', 'f'); }
156} {}
157
158do_test 3.1 {
159  list [catch {sqlite3_snapshot_open db main $snapshot } msg] $msg
160} {1 SQLITE_ERROR}
161
162do_test 3.2.1 {
163  execsql {
164    BEGIN;
165      SELECT * FROM t2;
166  }
167} {a b c d e f}
168do_test 3.2.2 {
169  list [catch {sqlite3_snapshot_open db main $snapshot } msg] $msg
170} {1 SQLITE_ERROR}
171
172do_test 3.2.3 {
173  execsql {
174    COMMIT;
175    BEGIN;
176      INSERT INTO t2 VALUES('g', 'h');
177  }
178  list [catch {sqlite3_snapshot_open db main $snapshot } msg] $msg
179} {1 SQLITE_ERROR}
180do_execsql_test 3.2.4 COMMIT
181
182do_test 3.3.1 {
183  execsql { PRAGMA journal_mode = DELETE }
184  execsql { BEGIN }
185  list [catch {sqlite3_snapshot_open db main $snapshot } msg] $msg
186} {1 SQLITE_ERROR}
187
188do_test 3.3.2 {
189  sqlite3_snapshot_free $snapshot
190  execsql COMMIT
191} {}
192
193#-------------------------------------------------------------------------
194# Check that SQLITE_BUSY_SNAPSHOT is returned if the specified snapshot
195# no longer exists because the wal file has been checkpointed.
196#
197#   1. Reading a snapshot from the middle of a wal file is not possible
198#      after the wal file has been checkpointed.
199#
200#   2. That a snapshot from the end of a wal file can not be read once
201#      the wal file has been wrapped.
202#
203do_execsql_test 4.1.0 {
204  PRAGMA journal_mode = wal;
205  CREATE TABLE t3(i, j);
206  INSERT INTO t3 VALUES('o', 't');
207  INSERT INTO t3 VALUES('t', 'f');
208  BEGIN;
209    SELECT * FROM t3;
210} {wal o t t f}
211
212do_test 4.1.1 {
213  set snapshot [sqlite3_snapshot_get db main]
214  execsql COMMIT
215} {}
216do_test 4.1.2 {
217  execsql {
218    INSERT INTO t3 VALUES('f', 's');
219    BEGIN;
220  }
221  sqlite3_snapshot_open db main $snapshot
222  execsql { SELECT * FROM t3 }
223} {o t t f}
224
225do_test 4.1.3 {
226  execsql {
227    COMMIT;
228    PRAGMA wal_checkpoint;
229    BEGIN;
230  }
231  list [catch {sqlite3_snapshot_open db main $snapshot} msg] $msg
232} {1 SQLITE_BUSY_SNAPSHOT}
233do_test 4.1.4 {
234  sqlite3_snapshot_free $snapshot
235  execsql COMMIT
236} {}
237
238do_test 4.2.1 {
239  execsql {
240    INSERT INTO t3 VALUES('s', 'e');
241    INSERT INTO t3 VALUES('n', 't');
242    BEGIN;
243      SELECT * FROM t3;
244  }
245} {o t t f f s s e n t}
246do_test 4.2.2 {
247  set snapshot [sqlite3_snapshot_get db main]
248  execsql {
249    COMMIT;
250    PRAGMA wal_checkpoint;
251    BEGIN;
252  }
253  sqlite3_snapshot_open db main $snapshot
254  execsql { SELECT * FROM t3 }
255} {o t t f f s s e n t}
256do_test 4.2.3 {
257  execsql {
258    COMMIT;
259    INSERT INTO t3 VALUES('e', 't');
260    BEGIN;
261  }
262  list [catch {sqlite3_snapshot_open db main $snapshot} msg] $msg
263} {1 SQLITE_BUSY_SNAPSHOT}
264do_test 4.2.4 {
265  sqlite3_snapshot_free $snapshot
266} {}
267
268#-------------------------------------------------------------------------
269# Check that SQLITE_BUSY is returned if a checkpoint is running when
270# sqlite3_snapshot_open() is called.
271#
272reset_db
273db close
274testvfs tvfs
275sqlite3 db test.db -vfs tvfs
276
277do_execsql_test 5.1 {
278  PRAGMA journal_mode = wal;
279  CREATE TABLE x1(x, xx, xxx);
280  INSERT INTO x1 VALUES('z', 'zz', 'zzz');
281  BEGIN;
282    SELECT * FROM x1;
283} {wal z zz zzz}
284
285do_test 5.2 {
286  set ::snapshot [sqlite3_snapshot_get db main]
287  sqlite3 db2 test.db -vfs tvfs
288  execsql {
289    INSERT INTO x1 VALUES('a', 'aa', 'aaa');
290    COMMIT;
291  }
292} {}
293
294set t53 0
295proc write_callback {args} {
296  do_test 5.3.[incr ::t53] {
297    execsql BEGIN
298    list [catch { sqlite3_snapshot_open db main $::snapshot } msg] $msg
299  } {1 SQLITE_BUSY}
300  catchsql COMMIT
301}
302
303tvfs filter xWrite
304tvfs script write_callback
305db2 eval { PRAGMA wal_checkpoint }
306db close
307db2 close
308tvfs delete
309sqlite3_snapshot_free $snapshot
310
311#-------------------------------------------------------------------------
312# Test that sqlite3_snapshot_get() may be called immediately after
313# "BEGIN; PRAGMA user_version;". And that sqlite3_snapshot_open() may
314# be called after opening the db handle and running the script
315# "PRAGMA user_version; BEGIN".
316reset_db
317do_execsql_test 6.1 {
318  PRAGMA journal_mode = wal;
319  CREATE TABLE x1(x, xx, xxx);
320  INSERT INTO x1 VALUES('z', 'zz', 'zzz');
321  BEGIN;
322    PRAGMA user_version;
323} {wal 0}
324do_test 6.2 {
325  set ::snapshot [sqlite3_snapshot_get db main]
326  execsql {
327    INSERT INTO x1 VALUES('a', 'aa', 'aaa');
328    COMMIT;
329  }
330} {}
331do_test 6.3 {
332  sqlite3 db2 test.db
333  db2 eval "PRAGMA user_version ; BEGIN"
334  sqlite3_snapshot_open db2 main $::snapshot
335  db2 eval { SELECT * FROM x1 }
336} {z zz zzz}
337sqlite3_snapshot_free $snapshot
338
339finish_test
340