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