xref: /sqlite-3.40.0/test/lock.test (revision c023e03e)
1# 2001 September 15
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 implements regression tests for SQLite library.  The
12# focus of this script is database locks.
13#
14# $Id: lock.test,v 1.18 2002/09/14 13:47:33 drh Exp $
15
16
17set testdir [file dirname $argv0]
18source $testdir/tester.tcl
19
20# Create an alternative connection to the database
21#
22do_test lock-1.0 {
23  sqlite db2 ./test.db
24  set dummy {}
25} {}
26do_test lock-1.1 {
27  execsql {SELECT name FROM sqlite_master WHERE type='table' ORDER BY name}
28} {}
29do_test lock-1.2 {
30  execsql {SELECT name FROM sqlite_master WHERE type='table' ORDER BY name} db2
31} {}
32do_test lock-1.3 {
33  execsql {CREATE TABLE t1(a int, b int)}
34  execsql {SELECT name FROM sqlite_master WHERE type='table' ORDER BY name}
35} {t1}
36do_test lock-1.4 {
37  catchsql {
38    SELECT name FROM sqlite_master WHERE type='table' ORDER BY name
39  } db2
40} {1 {database schema has changed}}
41do_test lock-1.5 {
42  catchsql {
43     SELECT name FROM sqlite_master WHERE type='table' ORDER BY name
44  } db2
45} {0 t1}
46
47do_test lock-1.6 {
48  execsql {INSERT INTO t1 VALUES(1,2)}
49  execsql {SELECT * FROM t1}
50} {1 2}
51do_test lock-1.7 {
52  execsql {SELECT * FROM t1} db2
53} {1 2}
54do_test lock-1.8 {
55  execsql {UPDATE t1 SET a=b, b=a} db2
56  execsql {SELECT * FROM t1} db2
57} {2 1}
58do_test lock-1.9 {
59  execsql {SELECT * FROM t1}
60} {2 1}
61do_test lock-1.10 {
62  execsql {BEGIN TRANSACTION}
63  execsql {SELECT * FROM t1}
64} {2 1}
65do_test lock-1.11 {
66  catchsql {SELECT * FROM t1} db2
67} {1 {database is locked}}
68do_test lock-1.12 {
69  execsql {ROLLBACK}
70  catchsql {SELECT * FROM t1}
71} {0 {2 1}}
72
73do_test lock-1.13 {
74  execsql {CREATE TABLE t2(x int, y int)}
75  execsql {INSERT INTO t2 VALUES(8,9)}
76  execsql {SELECT * FROM t2}
77} {8 9}
78do_test lock-1.14 {
79  catchsql {SELECT * FROM t1} db2
80} {1 {database schema has changed}}
81do_test lock-1.15 {
82  catchsql {SELECT * FROM t2} db2
83} {0 {8 9}}
84
85do_test lock-1.16 {
86  db eval {SELECT * FROM t1} qv {
87    set x [db eval {SELECT * FROM t1}]
88  }
89  set x
90} {2 1}
91do_test lock-1.17 {
92  db eval {SELECT * FROM t1} qv {
93    set x [db eval {SELECT * FROM t2}]
94  }
95  set x
96} {8 9}
97
98# You cannot UPDATE a table from within the callback of a SELECT
99# on that same table because the SELECT has the table locked.
100#
101do_test lock-1.18 {
102  db eval {SELECT * FROM t1} qv {
103    set r [catch {db eval {UPDATE t1 SET a=b, b=a}} msg]
104    lappend r $msg
105  }
106  set r
107} {1 {database table is locked}}
108
109# But you can UPDATE a different table from the one that is used in
110# the SELECT.
111#
112do_test lock-1.19 {
113  db eval {SELECT * FROM t1} qv {
114    set r [catch {db eval {UPDATE t2 SET x=y, y=x}} msg]
115    lappend r $msg
116  }
117  set r
118} {0 {}}
119do_test lock-1.20 {
120  execsql {SELECT * FROM t2}
121} {9 8}
122
123# It is possible to do a SELECT of the same table within the
124# callback of another SELECT on that same table because two
125# or more read-only cursors can be open at once.
126#
127do_test lock-1.21 {
128  db eval {SELECT * FROM t1} qv {
129    set r [catch {db eval {SELECT a FROM t1}} msg]
130    lappend r $msg
131  }
132  set r
133} {0 2}
134
135# Under UNIX you can do two SELECTs at once with different database
136# connections, because UNIX supports reader/writer locks.  Under windows,
137# this is not possible.
138#
139if {$::tcl_platform(platform)=="unix"} {
140  do_test lock-1.22 {
141    db eval {SELECT * FROM t1} qv {
142      set r [catch {db2 eval {SELECT a FROM t1}} msg]
143      lappend r $msg
144    }
145    set r
146  } {0 2}
147}
148integrity_check lock-1.23
149
150# If one thread has a transaction another thread cannot start
151# a transaction.
152#
153do_test lock-2.1 {
154  execsql {BEGIN TRANSACTION}
155  set r [catch {execsql {BEGIN TRANSACTION} db2} msg]
156  lappend r $msg
157} {1 {database is locked}}
158
159# Nor can the other thread do a query.
160#
161do_test lock-2.2 {
162  set r [catch {execsql {SELECT * FROM t2} db2} msg]
163  lappend r $msg
164} {1 {database is locked}}
165
166# If the other thread (the one that does not hold the transaction)
167# tries to start a transaction, we get a busy callback.
168#
169do_test lock-2.3 {
170  proc callback {args} {
171    set ::callback_value $args
172    break
173  }
174  set ::callback_value {}
175  db2 busy callback
176  set r [catch {execsql {BEGIN TRANSACTION} db2} msg]
177  lappend r $msg
178  lappend r $::callback_value
179} {1 {database is locked} {{} 1}}
180do_test lock-2.4 {
181  proc callback {file count} {
182    lappend ::callback_value $count
183    if {$count>4} break
184  }
185  set ::callback_value {}
186  db2 busy callback
187  set r [catch {execsql {BEGIN TRANSACTION} db2} msg]
188  lappend r $msg
189  lappend r $::callback_value
190} {1 {database is locked} {1 2 3 4 5}}
191do_test lock-2.5 {
192  proc callback {file count} {
193    lappend ::callback_value $count
194    if {$count>4} break
195  }
196  set ::callback_value {}
197  db2 busy callback
198  set r [catch {execsql {SELECT * FROM t1} db2} msg]
199  lappend r $msg
200  lappend r $::callback_value
201} {1 {database is locked} {1 2 3 4 5}}
202
203# In this test, the 3rd invocation of the busy callback causes
204# the first thread to release its transaction.  That allows the
205# second thread to continue.
206#
207do_test lock-2.6 {
208  proc callback {file count} {
209    lappend ::callback_value $count
210    if {$count>2} {
211      execsql {ROLLBACK}
212    }
213  }
214  set ::callback_value {}
215  db2 busy callback
216  set r [catch {execsql {SELECT * FROM t2} db2} msg]
217  lappend r $msg
218  lappend r $::callback_value
219} {0 {9 8} {1 2 3}}
220do_test lock-2.7 {
221  execsql {BEGIN TRANSACTION}
222  proc callback {file count} {
223    lappend ::callback_value $count
224    if {$count>2} {
225      execsql {ROLLBACK}
226    }
227  }
228  set ::callback_value {}
229  db2 busy callback
230  set r [catch {execsql {BEGIN TRANSACTION} db2} msg]
231  execsql {ROLLBACK} db2
232  lappend r $msg
233  lappend r $::callback_value
234} {0 {} {1 2 3}}
235integrity_check lock-2.8
236
237# Try to start two transactions in a row
238#
239do_test lock-3.1 {
240  execsql {BEGIN TRANSACTION}
241  set r [catch {execsql {BEGIN TRANSACTION}} msg]
242  execsql {ROLLBACK}
243  lappend r $msg
244} {1 {cannot start a transaction within a transaction}}
245integrity_check lock-3.2
246
247# Make sure the busy handler and error messages work when
248# opening a new pointer to the database while another pointer
249# has the database locked.
250#
251do_test lock-4.1 {
252  db2 close
253  catch {db eval ROLLBACK}
254  db eval BEGIN
255  sqlite db2 ./test.db
256  set rc [catch {db2 eval {SELECT * FROM t1}} msg]
257  lappend rc $msg
258} {1 {database is locked}}
259do_test lock-4.2 {
260  set ::callback_value {}
261  set rc [catch {db2 eval {SELECT * FROM t1}} msg]
262  lappend rc $msg $::callback_value
263} {1 {database is locked} {}}
264do_test lock-4.3 {
265  proc callback {file count} {
266    lappend ::callback_value $count
267    if {$count>4} break
268  }
269  db2 busy callback
270  set rc [catch {db2 eval {SELECT * FROM t1}} msg]
271  lappend rc $msg $::callback_value
272} {1 {database is locked} {1 2 3 4 5}}
273execsql {ROLLBACK}
274
275# When one thread is writing, other threads cannot read.  Except if the
276# writing thread is writing to its temporary tables, the other threads
277# can still read.
278#
279proc tx_exec {sql} {
280  db2 eval $sql
281}
282do_test lock-5.1 {
283  execsql {
284    SELECT * FROM t1
285  }
286} {2 1}
287do_test lock-5.2 {
288  db function tx_exec tx_exec
289  catchsql {
290    INSERT INTO t1(a,b) SELECT 3, tx_exec('SELECT y FROM t2 LIMIT 1');
291  }
292} {1 {database is locked}}
293do_test lock-5.3 {
294  execsql {
295    CREATE TEMP TABLE t3(x);
296    SELECT * FROM t3;
297  }
298} {}
299do_test lock-5.4 {
300  catchsql {
301    INSERT INTO t3 SELECT tx_exec('SELECT y FROM t2 LIMIT 1');
302  }
303} {0 {}}
304do_test lock-5.5 {
305  execsql {
306    SELECT * FROM t3;
307  }
308} {8}
309do_test lock-5.6 {
310  catchsql {
311    UPDATE t1 SET a=tx_exec('SELECT x FROM t2');
312  }
313} {1 {database is locked}}
314do_test lock-5.7 {
315  execsql {
316    SELECT * FROM t1;
317  }
318} {2 1}
319do_test lock-5.8 {
320  catchsql {
321    UPDATE t3 SET x=tx_exec('SELECT x FROM t2');
322  }
323} {0 {}}
324do_test lock-5.9 {
325  execsql {
326    SELECT * FROM t3;
327  }
328} {9}
329
330do_test lock-999.1 {
331  rename db2 {}
332} {}
333
334finish_test
335