# 2008 December 15 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # $Id: savepoint.test,v 1.4 2008/12/27 15:23:13 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl #---------------------------------------------------------------------- # The following tests - savepoint-1.* - test that the SAVEPOINT, RELEASE # and ROLLBACK TO comands are correctly parsed, and that the auto-commit # flag is correctly set and unset as a result. # do_test savepoint-1.1 { execsql { SAVEPOINT sp1; RELEASE sp1; } } {} do_test savepoint-1.2 { execsql { SAVEPOINT sp1; ROLLBACK TO sp1; } } {} do_test savepoint-1.3 { execsql { SAVEPOINT sp1 } db close } {} sqlite3 db test.db do_test savepoint-1.4.1 { execsql { SAVEPOINT sp1; SAVEPOINT sp2; RELEASE sp1; } sqlite3_get_autocommit db } {1} do_test savepoint-1.4.2 { execsql { SAVEPOINT sp1; SAVEPOINT sp2; RELEASE sp2; } sqlite3_get_autocommit db } {0} do_test savepoint-1.4.3 { execsql { RELEASE sp1 } sqlite3_get_autocommit db } {1} do_test savepoint-1.4.4 { execsql { SAVEPOINT sp1; SAVEPOINT sp2; ROLLBACK TO sp1; } sqlite3_get_autocommit db } {0} do_test savepoint-1.4.5 { execsql { RELEASE SAVEPOINT sp1 } sqlite3_get_autocommit db } {1} do_test savepoint-1.4.6 { execsql { SAVEPOINT sp1; SAVEPOINT sp2; SAVEPOINT sp3; ROLLBACK TO SAVEPOINT sp3; ROLLBACK TRANSACTION TO sp2; ROLLBACK TRANSACTION TO SAVEPOINT sp1; } sqlite3_get_autocommit db } {0} do_test savepoint-1.4.7 { execsql { RELEASE SAVEPOINT SP1 } sqlite3_get_autocommit db } {1} do_test savepoint-1.5 { execsql { SAVEPOINT sp1; ROLLBACK TO sp1; } } {} do_test savepoint-1.6 { execsql COMMIT } {} #------------------------------------------------------------------------ # These tests - savepoint-2.* - test rollbacks and releases of savepoints # with a very simple data set. # do_test savepoint-2.1 { execsql { CREATE TABLE t1(a, b, c); BEGIN; INSERT INTO t1 VALUES(1, 2, 3); SAVEPOINT one; UPDATE t1 SET a = 2, b = 3, c = 4; } execsql { SELECT * FROM t1 } } {2 3 4} do_test savepoint-2.2 { execsql { ROLLBACK TO one; } execsql { SELECT * FROM t1 } } {1 2 3} do_test savepoint-2.3 { execsql { INSERT INTO t1 VALUES(4, 5, 6); } execsql { SELECT * FROM t1 } } {1 2 3 4 5 6} do_test savepoint-2.4 { execsql { ROLLBACK TO one; } execsql { SELECT * FROM t1 } } {1 2 3} do_test savepoint-2.5 { execsql { INSERT INTO t1 VALUES(7, 8, 9); SAVEPOINT two; INSERT INTO t1 VALUES(10, 11, 12); } execsql { SELECT * FROM t1 } } {1 2 3 7 8 9 10 11 12} do_test savepoint-2.6 { execsql { ROLLBACK TO two; } execsql { SELECT * FROM t1 } } {1 2 3 7 8 9} do_test savepoint-2.7 { execsql { INSERT INTO t1 VALUES(10, 11, 12); } execsql { SELECT * FROM t1 } } {1 2 3 7 8 9 10 11 12} do_test savepoint-2.8 { execsql { ROLLBACK TO one; } execsql { SELECT * FROM t1 } } {1 2 3} do_test savepoint-2.9 { execsql { INSERT INTO t1 VALUES('a', 'b', 'c'); SAVEPOINT two; INSERT INTO t1 VALUES('d', 'e', 'f'); } execsql { SELECT * FROM t1 } } {1 2 3 a b c d e f} do_test savepoint-2.10 { execsql { RELEASE two; } execsql { SELECT * FROM t1 } } {1 2 3 a b c d e f} do_test savepoint-2.11 { execsql { ROLLBACK; } execsql { SELECT * FROM t1 } } {} #------------------------------------------------------------------------ # This block of tests - savepoint-3.* - test that when a transaction # savepoint is rolled back, locks are not released from database files. # And that when a transaction savepoint is released, they are released. # do_test savepoint-3.1 { execsql { SAVEPOINT "transaction" } execsql { PRAGMA lock_status } } {main unlocked temp closed} do_test savepoint-3.2 { execsql { INSERT INTO t1 VALUES(1, 2, 3) } execsql { PRAGMA lock_status } } {main reserved temp closed} do_test savepoint-3.3 { execsql { ROLLBACK TO "transaction" } execsql { PRAGMA lock_status } } {main reserved temp closed} do_test savepoint-3.4 { execsql { INSERT INTO t1 VALUES(1, 2, 3) } execsql { PRAGMA lock_status } } {main reserved temp closed} do_test savepoint-3.5 { execsql { RELEASE "transaction" } execsql { PRAGMA lock_status } } {main unlocked temp closed} #------------------------------------------------------------------------ # Test that savepoints that include schema modifications are handled # correctly. Test cases savepoint-4.*. # do_test savepoint-4.1 { execsql { CREATE TABLE t2(d, e, f); SELECT sql FROM sqlite_master; } } {{CREATE TABLE t1(a, b, c)} {CREATE TABLE t2(d, e, f)}} do_test savepoint-4.2 { execsql { BEGIN; CREATE TABLE t3(g,h); INSERT INTO t3 VALUES('I', 'II'); SAVEPOINT one; DROP TABLE t3; } } {} do_test savepoint-4.3 { execsql { CREATE TABLE t3(g, h, i); INSERT INTO t3 VALUES('III', 'IV', 'V'); } execsql {SELECT * FROM t3} } {III IV V} do_test savepoint-4.4 { execsql { ROLLBACK TO one; } execsql {SELECT * FROM t3} } {I II} do_test savepoint-4.5 { execsql { ROLLBACK; SELECT sql FROM sqlite_master; } } {{CREATE TABLE t1(a, b, c)} {CREATE TABLE t2(d, e, f)}} do_test savepoint-4.6 { execsql { BEGIN; INSERT INTO t1 VALUES('o', 't', 't'); SAVEPOINT sp1; CREATE TABLE t3(a, b, c); INSERT INTO t3 VALUES('z', 'y', 'x'); } execsql {SELECT * FROM t3} } {z y x} do_test savepoint-4.7 { execsql { ROLLBACK TO sp1; CREATE TABLE t3(a); INSERT INTO t3 VALUES('value'); } execsql {SELECT * FROM t3} } {value} do_test savepoint-4.8 { execsql COMMIT } {} #------------------------------------------------------------------------ # Test some logic errors to do with the savepoint feature. # do_test savepoint-5.1.1 { execsql { CREATE TABLE blobs(x); INSERT INTO blobs VALUES('a twentyeight character blob'); } set fd [db incrblob blobs x 1] puts -nonewline $fd "hello" catchsql {SAVEPOINT abc} } {1 {cannot open savepoint - SQL statements in progress}} do_test savepoint-5.1.2 { close $fd catchsql {SAVEPOINT abc} } {0 {}} do_test savepoint-5.2 { execsql {RELEASE abc} catchsql {RELEASE abc} } {1 {no such savepoint: abc}} do_test savepoint-5.3.1 { execsql {SAVEPOINT abc} catchsql {ROLLBACK TO def} } {1 {no such savepoint: def}} do_test savepoint-5.3.2 { execsql {SAVEPOINT def} set fd [db incrblob -readonly blobs x 1] catchsql {ROLLBACK TO def} } {1 {cannot rollback savepoint - SQL statements in progress}} do_test savepoint-5.3.3 { catchsql {RELEASE def} } {0 {}} do_test savepoint-5.3.4 { close $fd execsql {savepoint def} set fd [db incrblob blobs x 1] catchsql {release def} } {1 {cannot release savepoint - SQL statements in progress}} do_test savepoint-5.3.5 { close $fd execsql {release abc} } {} do_test savepoint-5.4.1 { execsql { SAVEPOINT main; INSERT INTO blobs VALUES('another blob'); } } {} do_test savepoint-5.4.2 { sqlite3 db2 test.db execsql { BEGIN ; SELECT * FROM blobs } db2 catchsql { RELEASE main } } {1 {database is locked}} do_test savepoint-5.4.3 { db2 close catchsql { RELEASE main } } {0 {}} do_test savepoint-5.4.4 { execsql { SELECT x FROM blobs WHERE rowid = 2 } } {{another blob}} #------------------------------------------------------------------------- # The following tests, savepoint-6.*, test an incr-vacuum inside of a # couple of nested savepoints. # ifcapable {autovacuum && pragma} { db close file delete -force test.db sqlite3 db test.db do_test savepoint-6.1 { execsql { PRAGMA auto_vacuum = incremental; CREATE TABLE t1(a, b, c); CREATE INDEX i1 ON t1(a, b); BEGIN; INSERT INTO t1 VALUES(randstr(10,400),randstr(10,400),randstr(10,400)); } set r "randstr(10,400)" for {set ii 0} {$ii < 10} {incr ii} { execsql "INSERT INTO t1 SELECT $r, $r, $r FROM t1" } execsql { COMMIT } } {} integrity_check savepoint-6.2 do_test savepoint-6.3 { execsql { PRAGMA cache_size = 10; BEGIN; UPDATE t1 SET a = randstr(10,10) WHERE (rowid%4)==0; SAVEPOINT one; DELETE FROM t1 WHERE rowid%2; PRAGMA incr_vacuum; SAVEPOINT two; INSERT INTO t1 SELECT randstr(10,400), randstr(10,400), c FROM t1; DELETE FROM t1 WHERE rowid%2; PRAGMA incr_vacuum; ROLLBACK TO one; COMMIT; } } {} integrity_check savepoint-6.4 } #------------------------------------------------------------------------- # The following tests, savepoint-7.*, attempt to break the logic # surrounding savepoints by growing and shrinking the database file. # db close file delete -force test.db sqlite3 db test.db do_test savepoint-7.1 { execsql { PRAGMA auto_vacuum = incremental; PRAGMA cache_size = 10; BEGIN; CREATE TABLE t1(a PRIMARY KEY, b); INSERT INTO t1(a) VALUES('alligator'); INSERT INTO t1(a) VALUES('angelfish'); INSERT INTO t1(a) VALUES('ant'); INSERT INTO t1(a) VALUES('antelope'); INSERT INTO t1(a) VALUES('ape'); INSERT INTO t1(a) VALUES('baboon'); INSERT INTO t1(a) VALUES('badger'); INSERT INTO t1(a) VALUES('bear'); INSERT INTO t1(a) VALUES('beetle'); INSERT INTO t1(a) VALUES('bird'); INSERT INTO t1(a) VALUES('bison'); UPDATE t1 SET b = randstr(1000,1000); UPDATE t1 SET b = b||randstr(1000,1000); UPDATE t1 SET b = b||randstr(1000,1000); UPDATE t1 SET b = b||randstr(10,1000); COMMIT; } expr ([execsql { PRAGMA page_count }] > 20) } {1} do_test savepoint-7.2.1 { execsql { BEGIN; SAVEPOINT one; CREATE TABLE t2(a, b); INSERT INTO t2 SELECT a, b FROM t1; ROLLBACK TO one; } execsql { PRAGMA integrity_check; } } {ok} do_test savepoint-7.2.2 { execsql { COMMIT; PRAGMA integrity_check; } } {ok} do_test savepoint-7.3.1 { execsql { CREATE TABLE t2(a, b); INSERT INTO t2 SELECT a, b FROM t1; } } {} do_test savepoint-7.3.2 { execsql { BEGIN; SAVEPOINT one; DELETE FROM t2; PRAGMA incremental_vacuum; SAVEPOINT two; INSERT INTO t2 SELECT a, b FROM t1; ROLLBACK TO two; COMMIT; } execsql { PRAGMA integrity_check } } {ok} do_test savepoint-7.4.1 { db close file delete -force test.db sqlite3 db test.db execsql { PRAGMA auto_vacuum = incremental; CREATE TABLE t1(a, b, PRIMARY KEY(a, b)); INSERT INTO t1 VALUES(randstr(1000,1000), randstr(1000,1000)); BEGIN; DELETE FROM t1; SAVEPOINT one; PRAGMA incremental_vacuum; ROLLBACK TO one; COMMIT; } execsql { PRAGMA integrity_check } } {ok} finish_test