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