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