1# 2008 December 15 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# 12# $Id: savepoint.test,v 1.7 2009/01/03 15:06:38 danielk1977 Exp $ 13 14set testdir [file dirname $argv0] 15source $testdir/tester.tcl 16 17 18#---------------------------------------------------------------------- 19# The following tests - savepoint-1.* - test that the SAVEPOINT, RELEASE 20# and ROLLBACK TO comands are correctly parsed, and that the auto-commit 21# flag is correctly set and unset as a result. 22# 23do_test savepoint-1.1 { 24 execsql { 25 SAVEPOINT sp1; 26 RELEASE sp1; 27 } 28} {} 29do_test savepoint-1.2 { 30 execsql { 31 SAVEPOINT sp1; 32 ROLLBACK TO sp1; 33 } 34} {} 35do_test savepoint-1.3 { 36 execsql { SAVEPOINT sp1 } 37 db close 38} {} 39sqlite3 db test.db 40do_test savepoint-1.4.1 { 41 execsql { 42 SAVEPOINT sp1; 43 SAVEPOINT sp2; 44 RELEASE sp1; 45 } 46 sqlite3_get_autocommit db 47} {1} 48do_test savepoint-1.4.2 { 49 execsql { 50 SAVEPOINT sp1; 51 SAVEPOINT sp2; 52 RELEASE sp2; 53 } 54 sqlite3_get_autocommit db 55} {0} 56do_test savepoint-1.4.3 { 57 execsql { RELEASE sp1 } 58 sqlite3_get_autocommit db 59} {1} 60do_test savepoint-1.4.4 { 61 execsql { 62 SAVEPOINT sp1; 63 SAVEPOINT sp2; 64 ROLLBACK TO sp1; 65 } 66 sqlite3_get_autocommit db 67} {0} 68do_test savepoint-1.4.5 { 69 execsql { RELEASE SAVEPOINT sp1 } 70 sqlite3_get_autocommit db 71} {1} 72do_test savepoint-1.4.6 { 73 execsql { 74 SAVEPOINT sp1; 75 SAVEPOINT sp2; 76 SAVEPOINT sp3; 77 ROLLBACK TO SAVEPOINT sp3; 78 ROLLBACK TRANSACTION TO sp2; 79 ROLLBACK TRANSACTION TO SAVEPOINT sp1; 80 } 81 sqlite3_get_autocommit db 82} {0} 83do_test savepoint-1.4.7 { 84 execsql { RELEASE SAVEPOINT SP1 } 85 sqlite3_get_autocommit db 86} {1} 87do_test savepoint-1.5 { 88 execsql { 89 SAVEPOINT sp1; 90 ROLLBACK TO sp1; 91 } 92} {} 93do_test savepoint-1.6 { 94 execsql COMMIT 95} {} 96 97#------------------------------------------------------------------------ 98# These tests - savepoint-2.* - test rollbacks and releases of savepoints 99# with a very simple data set. 100# 101 102do_test savepoint-2.1 { 103 execsql { 104 CREATE TABLE t1(a, b, c); 105 BEGIN; 106 INSERT INTO t1 VALUES(1, 2, 3); 107 SAVEPOINT one; 108 UPDATE t1 SET a = 2, b = 3, c = 4; 109 } 110 execsql { SELECT * FROM t1 } 111} {2 3 4} 112do_test savepoint-2.2 { 113 execsql { 114 ROLLBACK TO one; 115 } 116 execsql { SELECT * FROM t1 } 117} {1 2 3} 118do_test savepoint-2.3 { 119 execsql { 120 INSERT INTO t1 VALUES(4, 5, 6); 121 } 122 execsql { SELECT * FROM t1 } 123} {1 2 3 4 5 6} 124do_test savepoint-2.4 { 125 execsql { 126 ROLLBACK TO one; 127 } 128 execsql { SELECT * FROM t1 } 129} {1 2 3} 130 131 132do_test savepoint-2.5 { 133 execsql { 134 INSERT INTO t1 VALUES(7, 8, 9); 135 SAVEPOINT two; 136 INSERT INTO t1 VALUES(10, 11, 12); 137 } 138 execsql { SELECT * FROM t1 } 139} {1 2 3 7 8 9 10 11 12} 140do_test savepoint-2.6 { 141 execsql { 142 ROLLBACK TO two; 143 } 144 execsql { SELECT * FROM t1 } 145} {1 2 3 7 8 9} 146do_test savepoint-2.7 { 147 execsql { 148 INSERT INTO t1 VALUES(10, 11, 12); 149 } 150 execsql { SELECT * FROM t1 } 151} {1 2 3 7 8 9 10 11 12} 152do_test savepoint-2.8 { 153 execsql { 154 ROLLBACK TO one; 155 } 156 execsql { SELECT * FROM t1 } 157} {1 2 3} 158do_test savepoint-2.9 { 159 execsql { 160 INSERT INTO t1 VALUES('a', 'b', 'c'); 161 SAVEPOINT two; 162 INSERT INTO t1 VALUES('d', 'e', 'f'); 163 } 164 execsql { SELECT * FROM t1 } 165} {1 2 3 a b c d e f} 166do_test savepoint-2.10 { 167 execsql { 168 RELEASE two; 169 } 170 execsql { SELECT * FROM t1 } 171} {1 2 3 a b c d e f} 172do_test savepoint-2.11 { 173 execsql { 174 ROLLBACK; 175 } 176 execsql { SELECT * FROM t1 } 177} {} 178 179#------------------------------------------------------------------------ 180# This block of tests - savepoint-3.* - test that when a transaction 181# savepoint is rolled back, locks are not released from database files. 182# And that when a transaction savepoint is released, they are released. 183# 184do_test savepoint-3.1 { 185 execsql { SAVEPOINT "transaction" } 186 execsql { PRAGMA lock_status } 187} {main unlocked temp closed} 188 189do_test savepoint-3.2 { 190 execsql { INSERT INTO t1 VALUES(1, 2, 3) } 191 execsql { PRAGMA lock_status } 192} {main reserved temp closed} 193 194do_test savepoint-3.3 { 195 execsql { ROLLBACK TO "transaction" } 196 execsql { PRAGMA lock_status } 197} {main reserved temp closed} 198 199do_test savepoint-3.4 { 200 execsql { INSERT INTO t1 VALUES(1, 2, 3) } 201 execsql { PRAGMA lock_status } 202} {main reserved temp closed} 203 204do_test savepoint-3.5 { 205 execsql { RELEASE "transaction" } 206 execsql { PRAGMA lock_status } 207} {main unlocked temp closed} 208 209#------------------------------------------------------------------------ 210# Test that savepoints that include schema modifications are handled 211# correctly. Test cases savepoint-4.*. 212# 213do_test savepoint-4.1 { 214 execsql { 215 CREATE TABLE t2(d, e, f); 216 SELECT sql FROM sqlite_master; 217 } 218} {{CREATE TABLE t1(a, b, c)} {CREATE TABLE t2(d, e, f)}} 219do_test savepoint-4.2 { 220 execsql { 221 BEGIN; 222 CREATE TABLE t3(g,h); 223 INSERT INTO t3 VALUES('I', 'II'); 224 SAVEPOINT one; 225 DROP TABLE t3; 226 } 227} {} 228do_test savepoint-4.3 { 229 execsql { 230 CREATE TABLE t3(g, h, i); 231 INSERT INTO t3 VALUES('III', 'IV', 'V'); 232 } 233 execsql {SELECT * FROM t3} 234} {III IV V} 235do_test savepoint-4.4 { 236 execsql { ROLLBACK TO one; } 237 execsql {SELECT * FROM t3} 238} {I II} 239do_test savepoint-4.5 { 240 execsql { 241 ROLLBACK; 242 SELECT sql FROM sqlite_master; 243 } 244} {{CREATE TABLE t1(a, b, c)} {CREATE TABLE t2(d, e, f)}} 245 246do_test savepoint-4.6 { 247 execsql { 248 BEGIN; 249 INSERT INTO t1 VALUES('o', 't', 't'); 250 SAVEPOINT sp1; 251 CREATE TABLE t3(a, b, c); 252 INSERT INTO t3 VALUES('z', 'y', 'x'); 253 } 254 execsql {SELECT * FROM t3} 255} {z y x} 256do_test savepoint-4.7 { 257 execsql { 258 ROLLBACK TO sp1; 259 CREATE TABLE t3(a); 260 INSERT INTO t3 VALUES('value'); 261 } 262 execsql {SELECT * FROM t3} 263} {value} 264do_test savepoint-4.8 { 265 execsql COMMIT 266} {} 267 268#------------------------------------------------------------------------ 269# Test some logic errors to do with the savepoint feature. 270# 271 272do_test savepoint-5.1.1 { 273 execsql { 274 CREATE TABLE blobs(x); 275 INSERT INTO blobs VALUES('a twentyeight character blob'); 276 } 277 set fd [db incrblob blobs x 1] 278 puts -nonewline $fd "hello" 279 catchsql {SAVEPOINT abc} 280} {1 {cannot open savepoint - SQL statements in progress}} 281do_test savepoint-5.1.2 { 282 close $fd 283 catchsql {SAVEPOINT abc} 284} {0 {}} 285 286do_test savepoint-5.2 { 287 execsql {RELEASE abc} 288 catchsql {RELEASE abc} 289} {1 {no such savepoint: abc}} 290 291do_test savepoint-5.3.1 { 292 execsql {SAVEPOINT abc} 293 catchsql {ROLLBACK TO def} 294} {1 {no such savepoint: def}} 295do_test savepoint-5.3.2 { 296 execsql {SAVEPOINT def} 297 set fd [db incrblob -readonly blobs x 1] 298 catchsql {ROLLBACK TO def} 299} {1 {cannot rollback savepoint - SQL statements in progress}} 300do_test savepoint-5.3.3 { 301 catchsql {RELEASE def} 302} {0 {}} 303do_test savepoint-5.3.4 { 304 close $fd 305 execsql {savepoint def} 306 set fd [db incrblob blobs x 1] 307 catchsql {release def} 308} {1 {cannot release savepoint - SQL statements in progress}} 309do_test savepoint-5.3.5 { 310 close $fd 311 execsql {release abc} 312} {} 313 314do_test savepoint-5.4.1 { 315 execsql { 316 SAVEPOINT main; 317 INSERT INTO blobs VALUES('another blob'); 318 } 319} {} 320do_test savepoint-5.4.2 { 321 sqlite3 db2 test.db 322 execsql { BEGIN ; SELECT * FROM blobs } db2 323 catchsql { RELEASE main } 324} {1 {database is locked}} 325do_test savepoint-5.4.3 { 326 db2 close 327 catchsql { RELEASE main } 328} {0 {}} 329do_test savepoint-5.4.4 { 330 execsql { SELECT x FROM blobs WHERE rowid = 2 } 331} {{another blob}} 332 333#------------------------------------------------------------------------- 334# The following tests, savepoint-6.*, test an incr-vacuum inside of a 335# couple of nested savepoints. 336# 337ifcapable {autovacuum && pragma} { 338 db close 339 file delete -force test.db 340 sqlite3 db test.db 341 342 do_test savepoint-6.1 { 343 execsql { 344 PRAGMA auto_vacuum = incremental; 345 CREATE TABLE t1(a, b, c); 346 CREATE INDEX i1 ON t1(a, b); 347 BEGIN; 348 INSERT INTO t1 VALUES(randstr(10,400),randstr(10,400),randstr(10,400)); 349 } 350 set r "randstr(10,400)" 351 for {set ii 0} {$ii < 10} {incr ii} { 352 execsql "INSERT INTO t1 SELECT $r, $r, $r FROM t1" 353 } 354 execsql { COMMIT } 355 } {} 356 357 integrity_check savepoint-6.2 358 359 do_test savepoint-6.3 { 360 execsql { 361 PRAGMA cache_size = 10; 362 BEGIN; 363 UPDATE t1 SET a = randstr(10,10) WHERE (rowid%4)==0; 364 SAVEPOINT one; 365 DELETE FROM t1 WHERE rowid%2; 366 PRAGMA incr_vacuum; 367 SAVEPOINT two; 368 INSERT INTO t1 SELECT randstr(10,400), randstr(10,400), c FROM t1; 369 DELETE FROM t1 WHERE rowid%2; 370 PRAGMA incr_vacuum; 371 ROLLBACK TO one; 372 COMMIT; 373 } 374 } {} 375 376 integrity_check savepoint-6.4 377} 378 379#------------------------------------------------------------------------- 380# The following tests, savepoint-7.*, attempt to break the logic 381# surrounding savepoints by growing and shrinking the database file. 382# 383db close 384file delete -force test.db 385sqlite3 db test.db 386 387do_test savepoint-7.1 { 388 execsql { 389 PRAGMA auto_vacuum = incremental; 390 PRAGMA cache_size = 10; 391 BEGIN; 392 CREATE TABLE t1(a PRIMARY KEY, b); 393 INSERT INTO t1(a) VALUES('alligator'); 394 INSERT INTO t1(a) VALUES('angelfish'); 395 INSERT INTO t1(a) VALUES('ant'); 396 INSERT INTO t1(a) VALUES('antelope'); 397 INSERT INTO t1(a) VALUES('ape'); 398 INSERT INTO t1(a) VALUES('baboon'); 399 INSERT INTO t1(a) VALUES('badger'); 400 INSERT INTO t1(a) VALUES('bear'); 401 INSERT INTO t1(a) VALUES('beetle'); 402 INSERT INTO t1(a) VALUES('bird'); 403 INSERT INTO t1(a) VALUES('bison'); 404 UPDATE t1 SET b = randstr(1000,1000); 405 UPDATE t1 SET b = b||randstr(1000,1000); 406 UPDATE t1 SET b = b||randstr(1000,1000); 407 UPDATE t1 SET b = b||randstr(10,1000); 408 COMMIT; 409 } 410 expr ([execsql { PRAGMA page_count }] > 20) 411} {1} 412do_test savepoint-7.2.1 { 413 execsql { 414 BEGIN; 415 SAVEPOINT one; 416 CREATE TABLE t2(a, b); 417 INSERT INTO t2 SELECT a, b FROM t1; 418 ROLLBACK TO one; 419 } 420 execsql { 421 PRAGMA integrity_check; 422 } 423} {ok} 424do_test savepoint-7.2.2 { 425 execsql { 426 COMMIT; 427 PRAGMA integrity_check; 428 } 429} {ok} 430 431do_test savepoint-7.3.1 { 432 execsql { 433 CREATE TABLE t2(a, b); 434 INSERT INTO t2 SELECT a, b FROM t1; 435 } 436} {} 437do_test savepoint-7.3.2 { 438 execsql { 439 BEGIN; 440 SAVEPOINT one; 441 DELETE FROM t2; 442 PRAGMA incremental_vacuum; 443 SAVEPOINT two; 444 INSERT INTO t2 SELECT a, b FROM t1; 445 ROLLBACK TO two; 446 COMMIT; 447 } 448 execsql { PRAGMA integrity_check } 449} {ok} 450 451do_test savepoint-7.4.1 { 452 db close 453 file delete -force test.db 454 sqlite3 db test.db 455 execsql { 456 PRAGMA auto_vacuum = incremental; 457 CREATE TABLE t1(a, b, PRIMARY KEY(a, b)); 458 INSERT INTO t1 VALUES(randstr(1000,1000), randstr(1000,1000)); 459 BEGIN; 460 DELETE FROM t1; 461 SAVEPOINT one; 462 PRAGMA incremental_vacuum; 463 ROLLBACK TO one; 464 COMMIT; 465 } 466 467 execsql { PRAGMA integrity_check } 468} {ok} 469 470# Test oddly named and quoted savepoints. 471# 472do_test savepoint-8-1 { 473 execsql { SAVEPOINT "save1" } 474 execsql { RELEASE save1 } 475} {} 476do_test savepoint-8-2 { 477 execsql { SAVEPOINT "Including whitespace " } 478 execsql { RELEASE "including Whitespace " } 479} {} 480 481# Test that the authorization callback works. 482# 483ifcapable auth { 484 proc auth {args} { 485 eval lappend ::authdata $args 486 return SQLITE_OK 487 } 488 db auth auth 489 490 do_test savepoint-9.1 { 491 set ::authdata [list] 492 execsql { SAVEPOINT sp1 } 493 set ::authdata 494 } {SQLITE_SAVEPOINT BEGIN sp1 {} {}} 495 do_test savepoint-9.2 { 496 set ::authdata [list] 497 execsql { ROLLBACK TO sp1 } 498 set ::authdata 499 } {SQLITE_SAVEPOINT ROLLBACK sp1 {} {}} 500 do_test savepoint-9.3 { 501 set ::authdata [list] 502 execsql { RELEASE sp1 } 503 set ::authdata 504 } {SQLITE_SAVEPOINT RELEASE sp1 {} {}} 505 506 proc auth {args} { 507 eval lappend ::authdata $args 508 return SQLITE_DENY 509 } 510 db auth auth 511 512 do_test savepoint-9.4 { 513 set ::authdata [list] 514 set res [catchsql { SAVEPOINT sp1 }] 515 concat $::authdata $res 516 } {SQLITE_SAVEPOINT BEGIN sp1 {} {} 1 {not authorized}} 517 do_test savepoint-9.5 { 518 set ::authdata [list] 519 set res [catchsql { ROLLBACK TO sp1 }] 520 concat $::authdata $res 521 } {SQLITE_SAVEPOINT ROLLBACK sp1 {} {} 1 {not authorized}} 522 do_test savepoint-9.6 { 523 set ::authdata [list] 524 set res [catchsql { RELEASE sp1 }] 525 concat $::authdata $res 526 } {SQLITE_SAVEPOINT RELEASE sp1 {} {} 1 {not authorized}} 527 528 catch { db eval ROLLBACK } 529 db auth "" 530} 531 532#------------------------------------------------------------------------- 533# The following tests - savepoint-10.* - test the interaction of 534# savepoints and ATTACH statements. 535# 536 537# First make sure it is not possible to attach or detach a database while 538# a savepoint is open (it is not possible if any transaction is open). 539# 540do_test savepoint-10.1.1 { 541 catchsql { 542 SAVEPOINT one; 543 ATTACH 'test2.db' AS aux; 544 } 545} {1 {cannot ATTACH database within transaction}} 546do_test savepoint-10.1.2 { 547 execsql { 548 RELEASE one; 549 ATTACH 'test2.db' AS aux; 550 } 551 catchsql { 552 SAVEPOINT one; 553 DETACH aux; 554 } 555} {1 {cannot DETACH database within transaction}} 556do_test savepoint-10.1.3 { 557 execsql { 558 RELEASE one; 559 DETACH aux; 560 } 561} {} 562 563do_test savepoint-10.2.1 { 564 file delete -force test3.db 565 file delete -force test2.db 566 execsql { 567 ATTACH 'test2.db' AS aux1; 568 ATTACH 'test3.db' AS aux2; 569 DROP TABLE t1; 570 CREATE TABLE main.t1(x, y); 571 CREATE TABLE aux1.t2(x, y); 572 CREATE TABLE aux2.t3(x, y); 573 SELECT name FROM sqlite_master 574 UNION ALL 575 SELECT name FROM aux1.sqlite_master 576 UNION ALL 577 SELECT name FROM aux2.sqlite_master; 578 } 579} {t1 t2 t3} 580do_test savepoint-10.2.2 { 581 execsql { PRAGMA lock_status } 582} {main unlocked temp unlocked aux1 unlocked aux2 unlocked} 583 584do_test savepoint-10.2.3 { 585 execsql { 586 SAVEPOINT one; 587 INSERT INTO t1 VALUES(1, 2); 588 PRAGMA lock_status; 589 } 590} {main reserved temp unlocked aux1 unlocked aux2 unlocked} 591do_test savepoint-10.2.4 { 592 execsql { 593 INSERT INTO t3 VALUES(3, 4); 594 PRAGMA lock_status; 595 } 596} {main reserved temp unlocked aux1 unlocked aux2 reserved} 597do_test savepoint-10.2.5 { 598 execsql { 599 SAVEPOINT two; 600 INSERT INTO t2 VALUES(5, 6); 601 PRAGMA lock_status; 602 } 603} {main reserved temp unlocked aux1 reserved aux2 reserved} 604do_test savepoint-10.2.6 { 605 execsql { SELECT * FROM t2 } 606} {5 6} 607do_test savepoint-10.2.7 { 608 execsql { ROLLBACK TO two } 609 execsql { SELECT * FROM t2 } 610} {} 611do_test savepoint-10.2.8 { 612 execsql { PRAGMA lock_status } 613} {main reserved temp unlocked aux1 reserved aux2 reserved} 614do_test savepoint-10.2.9 { 615 execsql { SELECT 'a', * FROM t1 UNION ALL SELECT 'b', * FROM t3 } 616} {a 1 2 b 3 4} 617do_test savepoint-10.2.9 { 618 execsql { 619 INSERT INTO t2 VALUES(5, 6); 620 RELEASE one; 621 } 622 execsql { 623 SELECT * FROM t1; 624 SELECT * FROM t2; 625 SELECT * FROM t3; 626 } 627} {1 2 5 6 3 4} 628do_test savepoint-10.2.9 { 629 execsql { PRAGMA lock_status } 630} {main unlocked temp unlocked aux1 unlocked aux2 unlocked} 631 632do_test savepoint-10.2.10 { 633 execsql { 634 SAVEPOINT one; 635 INSERT INTO t1 VALUES('a', 'b'); 636 SAVEPOINT two; 637 INSERT INTO t2 VALUES('c', 'd'); 638 SAVEPOINT three; 639 INSERT INTO t3 VALUES('e', 'f'); 640 } 641 execsql { 642 SELECT * FROM t1; 643 SELECT * FROM t2; 644 SELECT * FROM t3; 645 } 646} {1 2 a b 5 6 c d 3 4 e f} 647do_test savepoint-10.2.11 { 648 execsql { ROLLBACK TO two } 649 execsql { 650 SELECT * FROM t1; 651 SELECT * FROM t2; 652 SELECT * FROM t3; 653 } 654} {1 2 a b 5 6 3 4} 655do_test savepoint-10.2.12 { 656 execsql { 657 INSERT INTO t3 VALUES('g', 'h'); 658 ROLLBACK TO two; 659 } 660 execsql { 661 SELECT * FROM t1; 662 SELECT * FROM t2; 663 SELECT * FROM t3; 664 } 665} {1 2 a b 5 6 3 4} 666do_test savepoint-10.2.13 { 667 execsql { ROLLBACK } 668 execsql { 669 SELECT * FROM t1; 670 SELECT * FROM t2; 671 SELECT * FROM t3; 672 } 673} {1 2 5 6 3 4} 674do_test savepoint-10.2.14 { 675 execsql { PRAGMA lock_status } 676} {main unlocked temp unlocked aux1 unlocked aux2 unlocked} 677 678#------------------------------------------------------------------------- 679# The following tests - savepoint-11.* - test the interaction of 680# savepoints and creating or dropping tables and indexes in 681# auto-vacuum mode. 682# 683do_test savepoint-11.1 { 684 db close 685 file delete -force test.db 686 sqlite3 db test.db 687 execsql { 688 PRAGMA auto_vacuum = full; 689 CREATE TABLE t1(a, b, UNIQUE(a, b)); 690 INSERT INTO t1 VALUES(1, randstr(1000,1000)); 691 INSERT INTO t1 VALUES(2, randstr(1000,1000)); 692 } 693} {} 694do_test savepoint-11.2 { 695 execsql { 696 SAVEPOINT one; 697 CREATE TABLE t2(a, b, UNIQUE(a, b)); 698 SAVEPOINT two; 699 CREATE TABLE t3(a, b, UNIQUE(a, b)); 700 } 701} {} 702integrity_check savepoint-11.3 703do_test savepoint-11.4 { 704 execsql { ROLLBACK TO two } 705} {} 706integrity_check savepoint-11.5 707do_test savepoint-11.6 { 708 execsql { 709 CREATE TABLE t3(a, b, UNIQUE(a, b)); 710 ROLLBACK TO one; 711 } 712} {} 713integrity_check savepoint-11.7 714do_test savepoint-11.6 { 715 execsql { ROLLBACK } 716 file size test.db 717} {8192} 718 719finish_test 720 721