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