sl@0: # 2001 September 15 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 sl@0: # focus of this script is database locks between competing processes. sl@0: # sl@0: # $Id: lock2.test,v 1.9 2007/12/13 21:54:11 drh Exp $ sl@0: sl@0: sl@0: set testdir [file dirname $argv0] sl@0: source $testdir/tester.tcl sl@0: sl@0: # Launch another testfixture process to be controlled by this one. A sl@0: # channel name is returned that may be passed as the first argument to proc sl@0: # 'testfixture' to execute a command. The child testfixture process is shut sl@0: # down by closing the channel. sl@0: proc launch_testfixture {} { sl@0: set prg [info nameofexec] sl@0: if {$prg eq ""} { sl@0: set prg [file join . testfixture] sl@0: } sl@0: set chan [open "|$prg tf_main.tcl" r+] sl@0: fconfigure $chan -buffering line sl@0: return $chan sl@0: } sl@0: sl@0: # Execute a command in a child testfixture process, connected by two-way sl@0: # channel $chan. Return the result of the command, or an error message. sl@0: proc testfixture {chan cmd} { sl@0: puts $chan $cmd sl@0: puts $chan OVER sl@0: set r "" sl@0: while { 1 } { sl@0: set line [gets $chan] sl@0: if { $line == "OVER" } { sl@0: return $r sl@0: } sl@0: append r $line sl@0: } sl@0: } sl@0: sl@0: # Write the main loop for the child testfixture processes into file sl@0: # tf_main.tcl. The parent (this script) interacts with the child processes sl@0: # via a two way pipe. The parent writes a script to the stdin of the child sl@0: # process, followed by the word "OVER" on a line of its own. The child sl@0: # process evaluates the script and writes the results to stdout, followed sl@0: # by an "OVER" of its own. sl@0: set f [open tf_main.tcl w] sl@0: puts $f { sl@0: set l [open log w] sl@0: set script "" sl@0: while {![eof stdin]} { sl@0: flush stdout sl@0: set line [gets stdin] sl@0: puts $l "READ $line" sl@0: if { $line == "OVER" } { sl@0: catch {eval $script} result sl@0: puts $result sl@0: puts $l "WRITE $result" sl@0: puts OVER sl@0: puts $l "WRITE OVER" sl@0: flush stdout sl@0: set script "" sl@0: } else { sl@0: append script $line sl@0: append script " ; " sl@0: } sl@0: } sl@0: close $l sl@0: } sl@0: close $f sl@0: sl@0: # Simple locking test case: sl@0: # sl@0: # lock2-1.1: Connect a second process to the database. sl@0: # lock2-1.2: Establish a RESERVED lock with this process. sl@0: # lock2-1.3: Get a SHARED lock with the second process. sl@0: # lock2-1.4: Try for a RESERVED lock with process 2. This fails. sl@0: # lock2-1.5: Try to upgrade the first process to EXCLUSIVE, this fails so sl@0: # it gets PENDING. sl@0: # lock2-1.6: Release the SHARED lock held by the second process. sl@0: # lock2-1.7: Attempt to reaquire a SHARED lock with the second process. sl@0: # this fails due to the PENDING lock. sl@0: # lock2-1.8: Ensure the first process can now upgrade to EXCLUSIVE. sl@0: # sl@0: do_test lock2-1.1 { sl@0: set ::tf1 [launch_testfixture] sl@0: testfixture $::tf1 "set sqlite_pending_byte $::sqlite_pending_byte" sl@0: testfixture $::tf1 { sl@0: sqlite3 db test.db -key xyzzy sl@0: db eval {select * from sqlite_master} sl@0: } sl@0: } {} sl@0: do_test lock2-1.1.1 { sl@0: execsql {pragma lock_status} sl@0: } {main unlocked temp closed} sl@0: sqlite3_soft_heap_limit 0 sl@0: do_test lock2-1.2 { sl@0: execsql { sl@0: BEGIN; sl@0: CREATE TABLE abc(a, b, c); sl@0: } sl@0: } {} sl@0: do_test lock2-1.3 { sl@0: testfixture $::tf1 { sl@0: db eval { sl@0: BEGIN; sl@0: SELECT * FROM sqlite_master; sl@0: } sl@0: } sl@0: } {} sl@0: do_test lock2-1.4 { sl@0: testfixture $::tf1 { sl@0: db eval { sl@0: CREATE TABLE def(d, e, f) sl@0: } sl@0: } sl@0: } {database is locked} sl@0: do_test lock2-1.5 { sl@0: catchsql { sl@0: COMMIT; sl@0: } sl@0: } {1 {database is locked}} sl@0: do_test lock2-1.6 { sl@0: testfixture $::tf1 { sl@0: db eval { sl@0: SELECT * FROM sqlite_master; sl@0: COMMIT; sl@0: } sl@0: } sl@0: } {} sl@0: do_test lock2-1.7 { sl@0: testfixture $::tf1 { sl@0: db eval { sl@0: BEGIN; sl@0: SELECT * FROM sqlite_master; sl@0: } sl@0: } sl@0: } {database is locked} sl@0: do_test lock2-1.8 { sl@0: catchsql { sl@0: COMMIT; sl@0: } sl@0: } {0 {}} sl@0: do_test lock2-1.9 { sl@0: execsql { sl@0: SELECT * FROM sqlite_master; sl@0: } sl@0: } "table abc abc [expr $AUTOVACUUM?3:2] {CREATE TABLE abc(a, b, c)}" sl@0: do_test lock2-1.10 { sl@0: testfixture $::tf1 { sl@0: db eval { sl@0: SELECT * FROM sqlite_master; sl@0: } sl@0: } sl@0: } "table abc abc [expr $AUTOVACUUM?3:2] {CREATE TABLE abc(a, b, c)}" sl@0: sl@0: catch {testfixture $::tf1 {db close}} sl@0: catch {close $::tf1} sl@0: sqlite3_soft_heap_limit $soft_limit sl@0: sl@0: finish_test