xref: /sqlite-3.40.0/test/lock.test (revision 4dcbdbff)
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.32 2005/03/29 03:11:00 danielk1977 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.
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 do get a busy callback
173# as long as we were not orginally holding a READ lock.
174#
175do_test lock-2.3.1 {
176  proc callback {count} {
177    set ::callback_value $count
178    break
179  }
180  set ::callback_value {}
181  db2 busy callback
182  # db2 does not hold a lock so we should get a busy callback here
183  set r [catch {execsql {UPDATE t1 SET a=b, b=a} db2} msg]
184  lappend r $msg
185  lappend r $::callback_value
186} {1 {database is locked} 0}
187do_test lock-2.3.2 {
188  set ::callback_value {}
189  execsql {BEGIN; SELECT rowid FROM sqlite_master LIMIT 1} db2
190  # This time db2 does hold a read lock.  No busy callback this time.
191  set r [catch {execsql {UPDATE t1 SET a=b, b=a} db2} msg]
192  lappend r $msg
193  lappend r $::callback_value
194} {1 {database is locked} {}}
195catch {execsql {ROLLBACK} db2}
196do_test lock-2.4.1 {
197  proc callback {count} {
198    lappend ::callback_value $count
199    if {$count>4} break
200  }
201  set ::callback_value {}
202  db2 busy callback
203  # We get a busy callback because db2 is not holding a lock
204  set r [catch {execsql {UPDATE t1 SET a=b, b=a} db2} msg]
205  lappend r $msg
206  lappend r $::callback_value
207} {1 {database is locked} {0 1 2 3 4 5}}
208do_test lock-2.4.2 {
209  proc callback {count} {
210    lappend ::callback_value $count
211    if {$count>4} break
212  }
213  set ::callback_value {}
214  db2 busy callback
215  execsql {BEGIN; SELECT rowid FROM sqlite_master LIMIT 1} db2
216  # No busy callback this time because we are holding a lock
217  set r [catch {execsql {UPDATE t1 SET a=b, b=a} db2} msg]
218  lappend r $msg
219  lappend r $::callback_value
220} {1 {database is locked} {}}
221catch {execsql {ROLLBACK} db2}
222do_test lock-2.5 {
223  proc callback {count} {
224    lappend ::callback_value $count
225    if {$count>4} break
226  }
227  set ::callback_value {}
228  db2 busy callback
229  set r [catch {execsql {SELECT * FROM t1} db2} msg]
230  lappend r $msg
231  lappend r $::callback_value
232} {0 {2 1} {}}
233execsql {ROLLBACK}
234
235# Test the built-in busy timeout handler
236#
237do_test lock-2.8 {
238  db2 timeout 400
239  execsql BEGIN
240  execsql {UPDATE t1 SET a = 0 WHERE 0}
241  catchsql {BEGIN EXCLUSIVE;} db2
242} {1 {database is locked}}
243do_test lock-2.9 {
244  db2 timeout 0
245  execsql COMMIT
246} {}
247integrity_check lock-2.10
248
249# Try to start two transactions in a row
250#
251do_test lock-3.1 {
252  execsql {BEGIN TRANSACTION}
253  set r [catch {execsql {BEGIN TRANSACTION}} msg]
254  execsql {ROLLBACK}
255  lappend r $msg
256} {1 {cannot start a transaction within a transaction}}
257integrity_check lock-3.2
258
259# Make sure the busy handler and error messages work when
260# opening a new pointer to the database while another pointer
261# has the database locked.
262#
263do_test lock-4.1 {
264  db2 close
265  catch {db eval ROLLBACK}
266  db eval BEGIN
267  db eval {UPDATE t1 SET a=0 WHERE 0}
268  sqlite3 db2 ./test.db
269  catchsql {UPDATE t1 SET a=0} db2
270} {1 {database is locked}}
271do_test lock-4.2 {
272  set ::callback_value {}
273  set rc [catch {db2 eval {UPDATE t1 SET a=0}} msg]
274  lappend rc $msg $::callback_value
275} {1 {database is locked} {}}
276do_test lock-4.3 {
277  proc callback {count} {
278    lappend ::callback_value $count
279    if {$count>4} break
280  }
281  db2 busy callback
282  set rc [catch {db2 eval {UPDATE t1 SET a=0}} msg]
283  lappend rc $msg $::callback_value
284} {1 {database is locked} {0 1 2 3 4 5}}
285execsql {ROLLBACK}
286
287# When one thread is writing, other threads cannot read.  Except if the
288# writing thread is writing to its temporary tables, the other threads
289# can still read.  -> Not so in 3.0.  One thread can read while another
290# holds a RESERVED lock.
291#
292proc tx_exec {sql} {
293  db2 eval $sql
294}
295do_test lock-5.1 {
296  execsql {
297    SELECT * FROM t1
298  }
299} {2 1}
300do_test lock-5.2 {
301  db function tx_exec tx_exec
302  catchsql {
303    INSERT INTO t1(a,b) SELECT 3, tx_exec('SELECT y FROM t2 LIMIT 1');
304  }
305} {0 {}}
306
307ifcapable tempdb {
308  do_test lock-5.3 {
309    execsql {
310      CREATE TEMP TABLE t3(x);
311      SELECT * FROM t3;
312    }
313  } {}
314  do_test lock-5.4 {
315    catchsql {
316      INSERT INTO t3 SELECT tx_exec('SELECT y FROM t2 LIMIT 1');
317    }
318  } {0 {}}
319  do_test lock-5.5 {
320    execsql {
321      SELECT * FROM t3;
322    }
323  } {8}
324  do_test lock-5.6 {
325    catchsql {
326      UPDATE t1 SET a=tx_exec('SELECT x FROM t2');
327    }
328  } {0 {}}
329  do_test lock-5.7 {
330    execsql {
331      SELECT * FROM t1;
332    }
333  } {9 1 9 8}
334  do_test lock-5.8 {
335    catchsql {
336      UPDATE t3 SET x=tx_exec('SELECT x FROM t2');
337    }
338  } {0 {}}
339  do_test lock-5.9 {
340    execsql {
341      SELECT * FROM t3;
342    }
343  } {9}
344}
345
346do_test lock-999.1 {
347  rename db2 {}
348} {}
349
350finish_test
351