sl@0: # 2007 March 24 sl@0: # sl@0: # The author disclaims copyright to this source code. In place of sl@0: # a legal notice, here is a blessing: sl@0: # sl@0: # May you do good and not evil. sl@0: # May you find forgiveness for yourself and forgive others. sl@0: # May you share freely, never taking more than you give. sl@0: # sl@0: #*********************************************************************** sl@0: # This file implements regression tests for SQLite library. The focus sl@0: # of these tests is exclusive access mode (i.e. the thing activated by sl@0: # "PRAGMA locking_mode = EXCLUSIVE"). sl@0: # sl@0: # $Id: exclusive.test,v 1.9 2008/09/24 14:03:43 danielk1977 Exp $ sl@0: sl@0: set testdir [file dirname $argv0] sl@0: source $testdir/tester.tcl sl@0: sl@0: ifcapable {!pager_pragmas} { sl@0: finish_test sl@0: return sl@0: } sl@0: sl@0: file delete -force test2.db-journal sl@0: file delete -force test2.db sl@0: file delete -force test3.db-journal sl@0: file delete -force test3.db sl@0: file delete -force test4.db-journal sl@0: file delete -force test4.db sl@0: sl@0: # The locking mode for the TEMP table is always "exclusive" for sl@0: # on-disk tables and "normal" for in-memory tables. sl@0: # sl@0: if {[info exists TEMP_STORE] && $TEMP_STORE>=2} { sl@0: set temp_mode normal sl@0: } else { sl@0: set temp_mode exclusive sl@0: } sl@0: sl@0: #---------------------------------------------------------------------- sl@0: # Test cases exclusive-1.X test the PRAGMA logic. sl@0: # sl@0: do_test exclusive-1.0 { sl@0: execsql { sl@0: pragma locking_mode; sl@0: pragma main.locking_mode; sl@0: pragma temp.locking_mode; sl@0: } sl@0: } [list normal normal $temp_mode] sl@0: do_test exclusive-1.1 { sl@0: execsql { sl@0: pragma locking_mode = exclusive; sl@0: } sl@0: } {exclusive} sl@0: do_test exclusive-1.2 { sl@0: execsql { sl@0: pragma locking_mode; sl@0: pragma main.locking_mode; sl@0: pragma temp.locking_mode; sl@0: } sl@0: } [list exclusive exclusive $temp_mode] sl@0: do_test exclusive-1.3 { sl@0: execsql { sl@0: pragma locking_mode = normal; sl@0: } sl@0: } {normal} sl@0: do_test exclusive-1.4 { sl@0: execsql { sl@0: pragma locking_mode; sl@0: pragma main.locking_mode; sl@0: pragma temp.locking_mode; sl@0: } sl@0: } [list normal normal $temp_mode] sl@0: do_test exclusive-1.5 { sl@0: execsql { sl@0: pragma locking_mode = invalid; sl@0: } sl@0: } {normal} sl@0: do_test exclusive-1.6 { sl@0: execsql { sl@0: pragma locking_mode; sl@0: pragma main.locking_mode; sl@0: pragma temp.locking_mode; sl@0: } sl@0: } [list normal normal $temp_mode] sl@0: ifcapable attach { sl@0: do_test exclusive-1.7 { sl@0: execsql { sl@0: pragma locking_mode = exclusive; sl@0: ATTACH 'test2.db' as aux; sl@0: } sl@0: execsql { sl@0: pragma main.locking_mode; sl@0: pragma aux.locking_mode; sl@0: } sl@0: } {exclusive exclusive} sl@0: do_test exclusive-1.8 { sl@0: execsql { sl@0: pragma main.locking_mode = normal; sl@0: } sl@0: execsql { sl@0: pragma main.locking_mode; sl@0: pragma temp.locking_mode; sl@0: pragma aux.locking_mode; sl@0: } sl@0: } [list normal $temp_mode exclusive] sl@0: do_test exclusive-1.9 { sl@0: execsql { sl@0: pragma locking_mode; sl@0: } sl@0: } {exclusive} sl@0: do_test exclusive-1.10 { sl@0: execsql { sl@0: ATTACH 'test3.db' as aux2; sl@0: } sl@0: execsql { sl@0: pragma main.locking_mode; sl@0: pragma aux.locking_mode; sl@0: pragma aux2.locking_mode; sl@0: } sl@0: } {normal exclusive exclusive} sl@0: do_test exclusive-1.11 { sl@0: execsql { sl@0: pragma aux.locking_mode = normal; sl@0: } sl@0: execsql { sl@0: pragma main.locking_mode; sl@0: pragma aux.locking_mode; sl@0: pragma aux2.locking_mode; sl@0: } sl@0: } {normal normal exclusive} sl@0: do_test exclusive-1.12 { sl@0: execsql { sl@0: pragma locking_mode = normal; sl@0: } sl@0: execsql { sl@0: pragma main.locking_mode; sl@0: pragma temp.locking_mode; sl@0: pragma aux.locking_mode; sl@0: pragma aux2.locking_mode; sl@0: } sl@0: } [list normal $temp_mode normal normal] sl@0: do_test exclusive-1.13 { sl@0: execsql { sl@0: ATTACH 'test4.db' as aux3; sl@0: } sl@0: execsql { sl@0: pragma main.locking_mode; sl@0: pragma temp.locking_mode; sl@0: pragma aux.locking_mode; sl@0: pragma aux2.locking_mode; sl@0: pragma aux3.locking_mode; sl@0: } sl@0: } [list normal $temp_mode normal normal normal] sl@0: sl@0: do_test exclusive-1.99 { sl@0: execsql { sl@0: DETACH aux; sl@0: DETACH aux2; sl@0: DETACH aux3; sl@0: } sl@0: } {} sl@0: } sl@0: sl@0: #---------------------------------------------------------------------- sl@0: # Test cases exclusive-2.X verify that connections in exclusive sl@0: # locking_mode do not relinquish locks. sl@0: # sl@0: do_test exclusive-2.0 { sl@0: execsql { sl@0: CREATE TABLE abc(a, b, c); sl@0: INSERT INTO abc VALUES(1, 2, 3); sl@0: PRAGMA locking_mode = exclusive; sl@0: } sl@0: } {exclusive} sl@0: do_test exclusive-2.1 { sl@0: sqlite3 db2 test.db sl@0: execsql { sl@0: INSERT INTO abc VALUES(4, 5, 6); sl@0: SELECT * FROM abc; sl@0: } db2 sl@0: } {1 2 3 4 5 6} sl@0: do_test exclusive-2.2 { sl@0: # This causes connection 'db' (in exclusive mode) to establish sl@0: # a shared-lock on the db. The other connection should now be sl@0: # locked out as a writer. sl@0: execsql { sl@0: SELECT * FROM abc; sl@0: } db sl@0: } {1 2 3 4 5 6} sl@0: do_test exclusive-2.4 { sl@0: execsql { sl@0: SELECT * FROM abc; sl@0: } db2 sl@0: } {1 2 3 4 5 6} sl@0: do_test exclusive-2.5 { sl@0: catchsql { sl@0: INSERT INTO abc VALUES(7, 8, 9); sl@0: } db2 sl@0: } {1 {database is locked}} sl@0: sqlite3_soft_heap_limit 0 sl@0: do_test exclusive-2.6 { sl@0: # Because connection 'db' only has a shared-lock, the other connection sl@0: # will be able to get a RESERVED, but will fail to upgrade to EXCLUSIVE. sl@0: execsql { sl@0: BEGIN; sl@0: INSERT INTO abc VALUES(7, 8, 9); sl@0: } db2 sl@0: catchsql { sl@0: COMMIT sl@0: } db2 sl@0: } {1 {database is locked}} sl@0: do_test exclusive-2.7 { sl@0: catchsql { sl@0: COMMIT sl@0: } db2 sl@0: } {1 {database is locked}} sl@0: do_test exclusive-2.8 { sl@0: execsql { sl@0: ROLLBACK; sl@0: } db2 sl@0: } {} sl@0: sqlite3_soft_heap_limit $soft_limit sl@0: sl@0: do_test exclusive-2.9 { sl@0: # Write the database to establish the exclusive lock with connection 'db. sl@0: execsql { sl@0: INSERT INTO abc VALUES(7, 8, 9); sl@0: } db sl@0: catchsql { sl@0: SELECT * FROM abc; sl@0: } db2 sl@0: } {1 {database is locked}} sl@0: do_test exclusive-2.10 { sl@0: # Changing the locking-mode does not release any locks. sl@0: execsql { sl@0: PRAGMA locking_mode = normal; sl@0: } db sl@0: catchsql { sl@0: SELECT * FROM abc; sl@0: } db2 sl@0: } {1 {database is locked}} sl@0: do_test exclusive-2.11 { sl@0: # After changing the locking mode, accessing the db releases locks. sl@0: execsql { sl@0: SELECT * FROM abc; sl@0: } db sl@0: execsql { sl@0: SELECT * FROM abc; sl@0: } db2 sl@0: } {1 2 3 4 5 6 7 8 9} sl@0: db2 close sl@0: sl@0: #---------------------------------------------------------------------- sl@0: # Tests exclusive-3.X - test that a connection in exclusive mode sl@0: # truncates instead of deletes the journal file when committing sl@0: # a transaction. sl@0: # sl@0: # These tests are not run on windows because the windows backend sl@0: # opens the journal file for exclusive access, preventing its contents sl@0: # from being inspected externally. sl@0: # sl@0: if {$tcl_platform(platform) != "windows"} { sl@0: proc filestate {fname} { sl@0: set exists 0 sl@0: set content 0 sl@0: if {[file exists $fname]} { sl@0: set exists 1 sl@0: set hdr [hexio_read $fname 0 28] sl@0: set content \ sl@0: [expr {$hdr!="00000000000000000000000000000000000000000000000000000000"}] sl@0: } sl@0: list $exists $content sl@0: } sl@0: do_test exclusive-3.0 { sl@0: filestate test.db-journal sl@0: } {0 0} sl@0: do_test exclusive-3.1 { sl@0: execsql { sl@0: PRAGMA locking_mode = exclusive; sl@0: BEGIN; sl@0: DELETE FROM abc; sl@0: } sl@0: filestate test.db-journal sl@0: } {1 1} sl@0: do_test exclusive-3.2 { sl@0: execsql { sl@0: COMMIT; sl@0: } sl@0: filestate test.db-journal sl@0: } {1 0} sl@0: do_test exclusive-3.3 { sl@0: execsql { sl@0: INSERT INTO abc VALUES('A', 'B', 'C'); sl@0: SELECT * FROM abc; sl@0: } sl@0: } {A B C} sl@0: do_test exclusive-3.4 { sl@0: execsql { sl@0: BEGIN; sl@0: UPDATE abc SET a = 1, b = 2, c = 3; sl@0: ROLLBACK; sl@0: SELECT * FROM abc; sl@0: } sl@0: } {A B C} sl@0: do_test exclusive-3.5 { sl@0: filestate test.db-journal sl@0: } {1 0} sl@0: do_test exclusive-3.6 { sl@0: execsql { sl@0: PRAGMA locking_mode = normal; sl@0: SELECT * FROM abc; sl@0: } sl@0: filestate test.db-journal sl@0: } {0 0} sl@0: } sl@0: sl@0: #---------------------------------------------------------------------- sl@0: # Tests exclusive-4.X - test that rollback works correctly when sl@0: # in exclusive-access mode. sl@0: # sl@0: sl@0: # The following procedure computes a "signature" for table "t3". If sl@0: # T3 changes in any way, the signature should change. sl@0: # sl@0: # This is used to test ROLLBACK. We gather a signature for t3, then sl@0: # make lots of changes to t3, then rollback and take another signature. sl@0: # The two signatures should be the same. sl@0: # sl@0: proc signature {} { sl@0: return [db eval {SELECT count(*), md5sum(x) FROM t3}] sl@0: } sl@0: sl@0: do_test exclusive-4.0 { sl@0: execsql { PRAGMA locking_mode = exclusive; } sl@0: execsql { PRAGMA default_cache_size = 10; } sl@0: execsql { sl@0: BEGIN; sl@0: CREATE TABLE t3(x TEXT); sl@0: INSERT INTO t3 VALUES(randstr(10,400)); sl@0: INSERT INTO t3 VALUES(randstr(10,400)); sl@0: INSERT INTO t3 SELECT randstr(10,400) FROM t3; sl@0: INSERT INTO t3 SELECT randstr(10,400) FROM t3; sl@0: INSERT INTO t3 SELECT randstr(10,400) FROM t3; sl@0: INSERT INTO t3 SELECT randstr(10,400) FROM t3; sl@0: COMMIT; sl@0: } sl@0: execsql {SELECT count(*) FROM t3;} sl@0: } {32} sl@0: sl@0: set ::X [signature] sl@0: do_test exclusive-4.1 { sl@0: execsql { sl@0: BEGIN; sl@0: DELETE FROM t3 WHERE random()%10!=0; sl@0: INSERT INTO t3 SELECT randstr(10,10)||x FROM t3; sl@0: INSERT INTO t3 SELECT randstr(10,10)||x FROM t3; sl@0: SELECT count(*) FROM t3; sl@0: ROLLBACK; sl@0: } sl@0: signature sl@0: } $::X sl@0: sl@0: do_test exclusive-4.2 { sl@0: execsql { sl@0: BEGIN; sl@0: DELETE FROM t3 WHERE random()%10!=0; sl@0: INSERT INTO t3 SELECT randstr(10,10)||x FROM t3; sl@0: DELETE FROM t3 WHERE random()%10!=0; sl@0: INSERT INTO t3 SELECT randstr(10,10)||x FROM t3; sl@0: ROLLBACK; sl@0: } sl@0: signature sl@0: } $::X sl@0: sl@0: do_test exclusive-4.3 { sl@0: execsql { sl@0: INSERT INTO t3 SELECT randstr(10,400) FROM t3 WHERE random()%10==0; sl@0: } sl@0: } {} sl@0: sl@0: do_test exclusive-4.4 { sl@0: catch {set ::X [signature]} sl@0: } {0} sl@0: do_test exclusive-4.5 { sl@0: execsql { sl@0: PRAGMA locking_mode = NORMAL; sl@0: DROP TABLE t3; sl@0: DROP TABLE abc; sl@0: } sl@0: } {normal} sl@0: sl@0: #---------------------------------------------------------------------- sl@0: # Tests exclusive-5.X - test that statement journals are truncated sl@0: # instead of deleted when in exclusive access mode. sl@0: # sl@0: sl@0: # Close and reopen the database so that the temp database is no sl@0: # longer active. sl@0: # sl@0: db close sl@0: sqlite db test.db sl@0: sl@0: sl@0: do_test exclusive-5.0 { sl@0: execsql { sl@0: CREATE TABLE abc(a UNIQUE, b UNIQUE, c UNIQUE); sl@0: BEGIN; sl@0: INSERT INTO abc VALUES(1, 2, 3); sl@0: INSERT INTO abc SELECT a+1, b+1, c+1 FROM abc; sl@0: } sl@0: } {} sl@0: do_test exclusive-5.1 { sl@0: # Three files are open: The db, journal and statement-journal. sl@0: set sqlite_open_file_count sl@0: } {3} sl@0: do_test exclusive-5.2 { sl@0: execsql { sl@0: COMMIT; sl@0: } sl@0: # One file open: the db. sl@0: set sqlite_open_file_count sl@0: } {1} sl@0: do_test exclusive-5.3 { sl@0: execsql { sl@0: PRAGMA locking_mode = exclusive; sl@0: BEGIN; sl@0: INSERT INTO abc VALUES(5, 6, 7); sl@0: } sl@0: # Two files open: the db and journal. sl@0: set sqlite_open_file_count sl@0: } {2} sl@0: do_test exclusive-5.4 { sl@0: execsql { sl@0: INSERT INTO abc SELECT a+10, b+10, c+10 FROM abc; sl@0: } sl@0: # Three files are open: The db, journal and statement-journal. sl@0: set sqlite_open_file_count sl@0: } {3} sl@0: do_test exclusive-5.5 { sl@0: execsql { sl@0: COMMIT; sl@0: } sl@0: # Three files are still open: The db, journal and statement-journal. sl@0: set sqlite_open_file_count sl@0: } {3} sl@0: do_test exclusive-5.6 { sl@0: execsql { sl@0: PRAGMA locking_mode = normal; sl@0: SELECT * FROM abc; sl@0: } sl@0: } {normal 1 2 3 2 3 4 5 6 7 11 12 13 12 13 14 15 16 17} sl@0: do_test exclusive-5.7 { sl@0: # Just the db open. sl@0: set sqlite_open_file_count sl@0: } {1} sl@0: sl@0: finish_test