modernc.org/cc@v1.0.1/v2/testdata/_sqlite/test/snapshot.test (about) 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 15 set testdir [file dirname $argv0] 16 source $testdir/tester.tcl 17 ifcapable !snapshot {finish_test; return} 18 set 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. 24 if {[permutation]=="inmemory_journal"} { 25 finish_test 26 return 27 } 28 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 } 44 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 } { 59 60 reset_db 61 eval $tcl 62 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 } 75 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 81 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 92 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 } {} 100 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} 109 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} 118 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} 126 127 do_test $tn.2.1.3 { 128 snapshot_free $snapshot 129 execsql COMMIT 130 } {} 131 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} 139 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} 147 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} 155 156 do_test $tn.2.2.3 { 157 snapshot_free $snapshot 158 execsql COMMIT 159 execsql COMMIT db2 160 db2 close 161 } {} 162 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} 177 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 } {} 187 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 } {} 209 210 do_test $tn.3.1 { 211 list [catch {snapshot_open db main $snapshot } msg] $msg 212 } {1 SQLITE_ERROR} 213 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} 223 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 233 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} 239 240 do_test $tn.$tn.3.3.2 { 241 snapshot_free $snapshot 242 execsql COMMIT 243 } {} 244 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} 263 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} 276 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 } {} 289 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 } {} 319 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 328 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} 336 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 } {} 345 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 } 354 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 362 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} 397 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} 404 405 snapshot_free $snapshot 406 407 #------------------------------------------------------------------------- 408 # The following tests investigate the sqlite3_snapshot_cmp() API. 409 # 410 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 } 419 420 catch { db2 close } 421 reset_db 422 423 do_execsql_test $tn.7.1 { 424 PRAGMA journal_mode = wal; 425 CREATE TABLE t1(x); 426 } wal 427 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 } {} 439 440 do_snapshot_cmp_test $tn.7.1.3 $p1 $p2 -1 441 snapshot_free $p1 442 snapshot_free $p2 443 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 } {} 463 464 do_snapshot_cmp_test $tn.7.2.3 $p1 $p2 -1 465 snapshot_free $p2 466 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 } {} 476 477 do_snapshot_cmp_test $tn.7.3.2 $p1 $p2 -1 478 snapshot_free $p1 479 snapshot_free $p2 480 } 481 482 finish_test