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