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