# 2020 June 30 # # 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. # #*********************************************************************** # This file test the busy handler # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/lock_common.tcl set testprefix busy2 do_multiclient_test tn { do_test 1.$tn.0 { sql2 { CREATE TABLE t1(a, b); PRAGMA journal_mode = wal; INSERT INTO t1 VALUES('A', 'B'); } } {wal} do_test 1.$tn.1 { code1 { db timeout 1000 } sql1 { SELECT * FROM t1 } } {A B} do_test 1.$tn.2 { sql2 { BEGIN; INSERT INTO t1 VALUES('C', 'D'); } } {} do_test 1.$tn.3 { set us [lindex [time { catch { sql1 { BEGIN EXCLUSIVE } } }] 0] expr {$us>950000 && $us<1500000} } {1} do_test 1.$tn.4 { sql2 { COMMIT } } {} } #------------------------------------------------------------------------- do_multiclient_test tn { # Make the db a WAL mode db. And add a table and a row to it. Then open # a second connection within process 1. Process 1 now has connections # [db] and [db1.2], process 2 has connection [db2] only. # # Configure all connections to use a 1000 ms timeout. # do_test 2.$tn.0 { code1 { sqlite3 db1.2 test.db } sql1 { PRAGMA auto_vacuum = off; PRAGMA journal_mode = wal; CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 2); } code2 { db2 timeout 1000 } code1 { db1.2 timeout 1000 db timeout 1000 db1.2 eval {SELECT * FROM t1} } } {1 2} # Take a read lock with [db] in process 1. # do_test 2.$tn.1 { sql1 { BEGIN; SELECT * FROM t1; } } {1 2} # Insert a row using [db2] in process 2. Then try a passive checkpoint. # It fails to checkpoint the final frame (due to the readlock taken by # [db]), and returns in less than 250ms. do_test 2.$tn.2 { sql2 { INSERT INTO t1 VALUES(3, 4) } set us [lindex [time { set res [code2 { db2 eval { PRAGMA wal_checkpoint } }] }] 0] list [expr $us < 250000] $res } {1 {0 4 3}} # Now try a FULL checkpoint with [db2]. It returns SQLITE_BUSY. And takes # over 950ms to do so. do_test 2.$tn.3 { set us [lindex [time { set res [code2 { db2 eval { PRAGMA wal_checkpoint = FULL } }] }] 0] list [expr $us > 950000] $res } {1 {1 4 3}} # Passive checkpoint with [db1.2] (process 1). No SQLITE_BUSY, returns # in under 250ms. do_test 2.$tn.4 { set us [lindex [time { set res [code1 { db1.2 eval { PRAGMA wal_checkpoint } }] }] 0] list [expr $us < 250000] $res } {1 {0 4 3}} # Full checkpoint with [db1.2] (process 1). SQLITE_BUSY returned in # a bit over 950ms. do_test 2.$tn.5 { set us [lindex [time { set res [code1 { db1.2 eval { PRAGMA wal_checkpoint = FULL } }] }] 0] list [expr $us > 950000] $res } {1 {1 4 3}} code1 { db1.2 close } } #------------------------------------------------------------------------- # Check that even if the busy-handler fails (returns zero) within a # call to sqlite3_prepare() (or _v2(), or _v3()), it is still invoked # the next time an SQLITE_BUSY is encountered. # do_multiclient_test tn { code1 { set ::busy_called 0 proc busy {args} { if {$::busy_called} { return 1 } set ::busy_called 1 return 0 } db busy busy } do_test 3.$tn.1 { sql2 { CREATE TABLE t1(x); BEGIN EXCLUSIVE; INSERT INTO t1 VALUES('x'); } } {} do_test 3.$tn.2 { set ::busy_called 0 list [catch { sql1 { SELECT * FROM t1 } } msg] $::busy_called } {1 1} do_test 3.$tn.3 { set ::busy_called 0 list [catch { sql1 { SELECT * FROM t1 } } msg] $::busy_called } {1 1} } finish_test