xref: /sqlite-3.40.0/test/busy2.test (revision cf2ad7ae)
1# 2020 June 30
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# This file test the busy handler
12#
13# TESTRUNNER: slow
14
15
16set testdir [file dirname $argv0]
17source $testdir/tester.tcl
18source $testdir/lock_common.tcl
19set testprefix busy2
20
21do_multiclient_test tn {
22  do_test 1.$tn.0 {
23    sql2 {
24      CREATE TABLE t1(a, b);
25      PRAGMA journal_mode = wal;
26      INSERT INTO t1 VALUES('A', 'B');
27    }
28  } {wal}
29
30  do_test 1.$tn.1 {
31    code1 { db timeout 1000 }
32    sql1 { SELECT * FROM t1 }
33  } {A B}
34
35  do_test 1.$tn.2 {
36    sql2 {
37      BEGIN;
38        INSERT INTO t1 VALUES('C', 'D');
39    }
40  } {}
41
42  do_test 1.$tn.3 {
43    set us [lindex [time { catch { sql1 { BEGIN EXCLUSIVE } } }] 0]
44    expr {$us>950000 && $us<1500000}
45  } {1}
46
47  do_test 1.$tn.4 {
48    sql2 {
49      COMMIT
50    }
51  } {}
52}
53
54#-------------------------------------------------------------------------
55
56do_multiclient_test tn {
57  # Make the db a WAL mode db. And add a table and a row to it. Then open
58  # a second connection within process 1. Process 1 now has connections
59  # [db] and [db1.2], process 2 has connection [db2] only.
60  #
61  # Configure all connections to use a 1000 ms timeout.
62  #
63  do_test 2.$tn.0 {
64    code1 {
65      sqlite3 db1.2 test.db
66    }
67    sql1 {
68      PRAGMA auto_vacuum = off;
69      PRAGMA journal_mode = wal;
70      CREATE TABLE t1(a, b);
71      INSERT INTO t1 VALUES(1, 2);
72    }
73    code2 {
74      db2 timeout 1000
75    }
76    code1 {
77      db1.2 timeout 1000
78      db    timeout 1000
79      db1.2 eval {SELECT * FROM t1}
80    }
81  } {1 2}
82
83  # Take a read lock with [db] in process 1.
84  #
85  do_test 2.$tn.1 {
86    sql1 {
87      BEGIN;
88        SELECT * FROM t1;
89    }
90  } {1 2}
91
92  # Insert a row using [db2] in process 2. Then try a passive checkpoint.
93  # It fails to checkpoint the final frame (due to the readlock taken by
94  # [db]), and returns in less than 250ms.
95  do_test 2.$tn.2 {
96    sql2 { INSERT INTO t1 VALUES(3, 4) }
97    set us [lindex [time {
98      set res [code2 { db2 eval { PRAGMA wal_checkpoint } }]
99    }] 0]
100    list [expr $us < 250000] $res
101  } {1 {0 4 3}}
102
103  # Now try a FULL checkpoint with [db2]. It returns SQLITE_BUSY. And takes
104  # over 950ms to do so.
105  do_test 2.$tn.3 {
106    set us [lindex [time {
107      set res [code2 { db2 eval { PRAGMA wal_checkpoint = FULL } }]
108    }] 0]
109    list [expr $us > 950000] $res
110  } {1 {1 4 3}}
111
112  # Passive checkpoint with [db1.2] (process 1). No SQLITE_BUSY, returns
113  # in under 250ms.
114  do_test 2.$tn.4 {
115    set us [lindex [time {
116      set res [code1 { db1.2 eval { PRAGMA wal_checkpoint } }]
117    }] 0]
118    list [expr $us < 250000] $res
119  } {1 {0 4 3}}
120
121  # Full checkpoint with [db1.2] (process 1). SQLITE_BUSY returned in
122  # a bit over 950ms.
123  do_test 2.$tn.5 {
124    set us [lindex [time {
125      set res [code1 { db1.2 eval { PRAGMA wal_checkpoint = FULL } }]
126    }] 0]
127    list [expr $us > 950000] $res
128  } {1 {1 4 3}}
129
130  code1 {
131    db1.2 close
132  }
133}
134
135#-------------------------------------------------------------------------
136# Check that even if the busy-handler fails (returns zero) within a
137# call to sqlite3_prepare() (or _v2(), or _v3()), it is still invoked
138# the next time an SQLITE_BUSY is encountered.
139#
140do_multiclient_test tn {
141  code1 {
142    set ::busy_called 0
143    proc busy {args} {
144      if {$::busy_called} { return 1 }
145      set ::busy_called 1
146      return 0
147    }
148    db busy busy
149  }
150
151  do_test 3.$tn.1 {
152    sql2 {
153      CREATE TABLE t1(x);
154      BEGIN EXCLUSIVE;
155        INSERT INTO t1 VALUES('x');
156    }
157  } {}
158
159  do_test 3.$tn.2 {
160    set ::busy_called 0
161    list [catch { sql1 { SELECT * FROM t1 } } msg] $::busy_called
162  } {1 1}
163
164  do_test 3.$tn.3 {
165    set ::busy_called 0
166    list [catch { sql1 { SELECT * FROM t1 } } msg] $::busy_called
167  } {1 1}
168
169}
170
171finish_test
172