13c8bf55aSdrh# 2003 July 1 23c8bf55aSdrh# 33c8bf55aSdrh# The author disclaims copyright to this source code. In place of 43c8bf55aSdrh# a legal notice, here is a blessing: 53c8bf55aSdrh# 63c8bf55aSdrh# May you do good and not evil. 73c8bf55aSdrh# May you find forgiveness for yourself and forgive others. 83c8bf55aSdrh# May you share freely, never taking more than you give. 93c8bf55aSdrh# 103c8bf55aSdrh#*********************************************************************** 113c8bf55aSdrh# This file implements regression tests for SQLite library. The 123c8bf55aSdrh# focus of this script is testing the ATTACH and DETACH commands 133c8bf55aSdrh# and related functionality. 143c8bf55aSdrh# 1585b623f2Sdrh# $Id: attach2.test,v 1.38 2007/12/13 21:54:11 drh Exp $ 163c8bf55aSdrh# 173c8bf55aSdrh 183c8bf55aSdrhset testdir [file dirname $argv0] 193c8bf55aSdrhsource $testdir/tester.tcl 203c8bf55aSdrh 215a8f9374Sdanielk1977ifcapable !attach { 225a8f9374Sdanielk1977 finish_test 235a8f9374Sdanielk1977 return 245a8f9374Sdanielk1977} 255a8f9374Sdanielk1977 263c8bf55aSdrh# Ticket #354 273c8bf55aSdrh# 282ac3ee97Sdrh# Databases test.db and test2.db contain identical schemas. Make 292ac3ee97Sdrh# sure we can attach test2.db from test.db. 302ac3ee97Sdrh# 313c8bf55aSdrhdo_test attach2-1.1 { 323c8bf55aSdrh db eval { 333c8bf55aSdrh CREATE TABLE t1(a,b); 343c8bf55aSdrh CREATE INDEX x1 ON t1(a); 353c8bf55aSdrh } 36fda06befSmistachkin forcedelete test2.db 37fda06befSmistachkin forcedelete test2.db-journal 38ef4ac8f9Sdrh sqlite3 db2 test2.db 393c8bf55aSdrh db2 eval { 403c8bf55aSdrh CREATE TABLE t1(a,b); 413c8bf55aSdrh CREATE INDEX x1 ON t1(a); 423c8bf55aSdrh } 433c8bf55aSdrh catchsql { 443c8bf55aSdrh ATTACH 'test2.db' AS t2; 453c8bf55aSdrh } 463c8bf55aSdrh} {0 {}} 473c8bf55aSdrh 48447623d9Sdrh# Ticket #514 49447623d9Sdrh# 50447623d9Sdrhproc db_list {db} { 51447623d9Sdrh set list {} 52447623d9Sdrh foreach {idx name file} [execsql {PRAGMA database_list} $db] { 53447623d9Sdrh lappend list $idx $name 54447623d9Sdrh } 55447623d9Sdrh return $list 56447623d9Sdrh} 57447623d9Sdrhdb eval {DETACH t2} 58447623d9Sdrhdo_test attach2-2.1 { 592ac3ee97Sdrh # lock test2.db then try to attach it. This is no longer an error because 602ac3ee97Sdrh # db2 just RESERVES the database. It does not obtain a write-lock until 612ac3ee97Sdrh # we COMMIT. 62447623d9Sdrh db2 eval {BEGIN} 631d850a72Sdanielk1977 db2 eval {UPDATE t1 SET a = 0 WHERE 0} 64447623d9Sdrh catchsql { 65447623d9Sdrh ATTACH 'test2.db' AS t2; 66447623d9Sdrh } 67447623d9Sdrh} {0 {}} 6827188fb5Sdanielk1977ifcapable schema_pragmas { 692ac3ee97Sdrhdo_test attach2-2.2 { 702ac3ee97Sdrh # make sure test2.db did get attached. 71447623d9Sdrh db_list db 722b74d70aSdrh} {0 main 2 t2} 7327188fb5Sdanielk1977} ;# ifcapable schema_pragmas 742ac3ee97Sdrhdb2 eval {COMMIT} 752ac3ee97Sdrh 76447623d9Sdrhdo_test attach2-2.5 { 772ac3ee97Sdrh # Make sure we can read test2.db from db 78447623d9Sdrh catchsql { 79447623d9Sdrh SELECT name FROM t2.sqlite_master; 80447623d9Sdrh } 81447623d9Sdrh} {0 {t1 x1}} 82447623d9Sdrhdo_test attach2-2.6 { 832ac3ee97Sdrh # lock test2.db and try to read from it. This should still work because 842ac3ee97Sdrh # the lock is only a RESERVED lock which does not prevent reading. 852ac3ee97Sdrh # 86447623d9Sdrh db2 eval BEGIN 871d850a72Sdanielk1977 db2 eval {UPDATE t1 SET a = 0 WHERE 0} 88447623d9Sdrh catchsql { 89447623d9Sdrh SELECT name FROM t2.sqlite_master; 90447623d9Sdrh } 912ac3ee97Sdrh} {0 {t1 x1}} 92447623d9Sdrhdo_test attach2-2.7 { 93447623d9Sdrh # but we can still read from test1.db even though test2.db is locked. 94447623d9Sdrh catchsql { 95447623d9Sdrh SELECT name FROM main.sqlite_master; 96447623d9Sdrh } 97447623d9Sdrh} {0 {t1 x1}} 98447623d9Sdrhdo_test attach2-2.8 { 99447623d9Sdrh # start a transaction on test.db even though test2.db is locked. 100447623d9Sdrh catchsql { 101447623d9Sdrh BEGIN; 102447623d9Sdrh INSERT INTO t1 VALUES(8,9); 103447623d9Sdrh } 104447623d9Sdrh} {0 {}} 105447623d9Sdrhdo_test attach2-2.9 { 106447623d9Sdrh execsql { 107447623d9Sdrh SELECT * FROM t1 108447623d9Sdrh } 109447623d9Sdrh} {8 9} 110447623d9Sdrhdo_test attach2-2.10 { 111447623d9Sdrh # now try to write to test2.db. the write should fail 112447623d9Sdrh catchsql { 113447623d9Sdrh INSERT INTO t2.t1 VALUES(1,2); 114447623d9Sdrh } 115447623d9Sdrh} {1 {database is locked}} 116447623d9Sdrhdo_test attach2-2.11 { 117447623d9Sdrh # when the write failed in the previous test, the transaction should 118447623d9Sdrh # have rolled back. 1191d850a72Sdanielk1977 # 1201d850a72Sdanielk1977 # Update for version 3: A transaction is no longer rolled back if a 1211d850a72Sdanielk1977 # database is found to be busy. 1221d850a72Sdanielk1977 execsql {rollback} 1238ef83ffeSdrh db2 eval ROLLBACK 124447623d9Sdrh execsql { 125447623d9Sdrh SELECT * FROM t1 126447623d9Sdrh } 127447623d9Sdrh} {} 128447623d9Sdrhdo_test attach2-2.12 { 129447623d9Sdrh catchsql { 130447623d9Sdrh COMMIT 131447623d9Sdrh } 132447623d9Sdrh} {1 {cannot commit - no transaction is active}} 133447623d9Sdrh 1342ac3ee97Sdrh# Ticket #574: Make sure it works using the non-callback API 1350bca3530Sdrh# 1360bca3530Sdrhdo_test attach2-3.1 { 137dddca286Sdrh set DB [sqlite3_connection_pointer db] 1384ad1713cSdanielk1977 set rc [catch {sqlite3_prepare $DB "ATTACH 'test2.db' AS t2" -1 TAIL} VM] 1390bca3530Sdrh if {$rc} {lappend rc $VM} 140f744bb56Sdanielk1977 sqlite3_step $VM 141106bb236Sdanielk1977 sqlite3_finalize $VM 1420bca3530Sdrh set rc 1430bca3530Sdrh} {0} 1440bca3530Sdrhdo_test attach2-3.2 { 1454ad1713cSdanielk1977 set rc [catch {sqlite3_prepare $DB "DETACH t2" -1 TAIL} VM] 1460bca3530Sdrh if {$rc} {lappend rc $VM} 147f744bb56Sdanielk1977 sqlite3_step $VM 148106bb236Sdanielk1977 sqlite3_finalize $VM 1490bca3530Sdrh set rc 1500bca3530Sdrh} {0} 1510bca3530Sdrh 1529cb733c3Sdrhdb close 1533c8bf55aSdrhfor {set i 2} {$i<=15} {incr i} { 1543c8bf55aSdrh catch {db$i close} 1553c8bf55aSdrh} 156a6abd041Sdrh 157a6abd041Sdrh# A procedure to verify the status of locks on a database. 158a6abd041Sdrh# 159a6abd041Sdrhproc lock_status {testnum db expected_result} { 16053c0f748Sdanielk1977 # If the database was compiled with OMIT_TEMPDB set, then 16153c0f748Sdanielk1977 # the lock_status list will not contain an entry for the temp 16285b623f2Sdrh # db. But the test code doesn't know this, so its easiest 163aef0bf64Sdanielk1977 # to filter it out of the $expected_result list here. 16453c0f748Sdanielk1977 ifcapable !tempdb { 16553c0f748Sdanielk1977 set expected_result [concat \ 16653c0f748Sdanielk1977 [lrange $expected_result 0 1] \ 16753c0f748Sdanielk1977 [lrange $expected_result 4 end] \ 16853c0f748Sdanielk1977 ] 16953c0f748Sdanielk1977 } 170a6abd041Sdrh do_test attach2-$testnum [subst { 171d5a71b5dSdrh $db cache flush ;# The lock_status pragma should not be cached 172a6abd041Sdrh execsql {PRAGMA lock_status} $db 173a6abd041Sdrh }] $expected_result 174a6abd041Sdrh} 1752ac3ee97Sdrhset sqlite_os_trace 0 1763a81de11Sdanielk1977 1773a81de11Sdanielk1977# Tests attach2-4.* test that read-locks work correctly with attached 1783a81de11Sdanielk1977# databases. 1793a81de11Sdanielk1977do_test attach2-4.1 { 180ef4ac8f9Sdrh sqlite3 db test.db 181ef4ac8f9Sdrh sqlite3 db2 test.db 1823a81de11Sdanielk1977 execsql {ATTACH 'test2.db' as file2} 1833a81de11Sdanielk1977 execsql {ATTACH 'test2.db' as file2} db2 1843a81de11Sdanielk1977} {} 1853a81de11Sdanielk1977 186dc3ff9c3Sdrhlock_status 4.1.1 db {main unlocked temp closed file2 unlocked} 187dc3ff9c3Sdrhlock_status 4.1.2 db2 {main unlocked temp closed file2 unlocked} 188a6abd041Sdrh 1893a81de11Sdanielk1977do_test attach2-4.2 { 1902ac3ee97Sdrh # Handle 'db' read-locks test.db 1913a81de11Sdanielk1977 execsql {BEGIN} 1923a81de11Sdanielk1977 execsql {SELECT * FROM t1} 193faa57accSdrh # Lock status: 194faa57accSdrh # db - shared(main) 195faa57accSdrh # db2 - 1963a81de11Sdanielk1977} {} 197a6abd041Sdrh 198dc3ff9c3Sdrhlock_status 4.2.1 db {main shared temp closed file2 unlocked} 199dc3ff9c3Sdrhlock_status 4.2.2 db2 {main unlocked temp closed file2 unlocked} 200a6abd041Sdrh 2013a81de11Sdanielk1977do_test attach2-4.3 { 2022ac3ee97Sdrh # The read lock held by db does not prevent db2 from reading test.db 2033a81de11Sdanielk1977 execsql {SELECT * FROM t1} db2 2043a81de11Sdanielk1977} {} 205a6abd041Sdrh 206dc3ff9c3Sdrhlock_status 4.3.1 db {main shared temp closed file2 unlocked} 207dc3ff9c3Sdrhlock_status 4.3.2 db2 {main unlocked temp closed file2 unlocked} 208a6abd041Sdrh 2093a81de11Sdanielk1977do_test attach2-4.4 { 210faa57accSdrh # db is holding a read lock on test.db, so we should not be able 2112ac3ee97Sdrh # to commit a write to test.db from db2 212faa57accSdrh catchsql { 2133a81de11Sdanielk1977 INSERT INTO t1 VALUES(1, 2) 2143a81de11Sdanielk1977 } db2 2153a81de11Sdanielk1977} {1 {database is locked}} 216a6abd041Sdrh 217dc3ff9c3Sdrhlock_status 4.4.1 db {main shared temp closed file2 unlocked} 21834f4732bSdrhlock_status 4.4.2 db2 {main unlocked temp closed file2 unlocked} 219a6abd041Sdrh 220b771228eSdrh# We have to make sure that the cache_size and the soft_heap_limit 221b771228eSdrh# are large enough to hold the entire change in memory. If either 222b771228eSdrh# is set too small, then changes will spill to the database, forcing 223b771228eSdrh# a reserved lock to promote to exclusive. That will mess up our 224b771228eSdrh# test results. 225b771228eSdrh 226b771228eSdrhset soft_limit [sqlite3_soft_heap_limit 0] 227b771228eSdrh 228b771228eSdrh 2293a81de11Sdanielk1977do_test attach2-4.5 { 2302ac3ee97Sdrh # Handle 'db2' reserves file2. 2313a81de11Sdanielk1977 execsql {BEGIN} db2 2323a81de11Sdanielk1977 execsql {INSERT INTO file2.t1 VALUES(1, 2)} db2 233faa57accSdrh # Lock status: 234faa57accSdrh # db - shared(main) 235faa57accSdrh # db2 - reserved(file2) 2363a81de11Sdanielk1977} {} 237a6abd041Sdrh 238dc3ff9c3Sdrhlock_status 4.5.1 db {main shared temp closed file2 unlocked} 23934f4732bSdrhlock_status 4.5.2 db2 {main unlocked temp closed file2 reserved} 240a6abd041Sdrh 2412ac3ee97Sdrhdo_test attach2-4.6.1 { 2422ac3ee97Sdrh # Reads are allowed against a reserved database. 2432ac3ee97Sdrh catchsql { 2443a81de11Sdanielk1977 SELECT * FROM file2.t1; 2453a81de11Sdanielk1977 } 246faa57accSdrh # Lock status: 247faa57accSdrh # db - shared(main), shared(file2) 248faa57accSdrh # db2 - reserved(file2) 2492ac3ee97Sdrh} {0 {}} 250a6abd041Sdrh 251dc3ff9c3Sdrhlock_status 4.6.1.1 db {main shared temp closed file2 shared} 25234f4732bSdrhlock_status 4.6.1.2 db2 {main unlocked temp closed file2 reserved} 253a6abd041Sdrh 2542ac3ee97Sdrhdo_test attach2-4.6.2 { 2552ac3ee97Sdrh # Writes against a reserved database are not allowed. 2562ac3ee97Sdrh catchsql { 2572ac3ee97Sdrh UPDATE file2.t1 SET a=0; 2582ac3ee97Sdrh } 2593a81de11Sdanielk1977} {1 {database is locked}} 260a6abd041Sdrh 26134f4732bSdrhlock_status 4.6.2.1 db {main shared temp closed file2 shared} 26234f4732bSdrhlock_status 4.6.2.2 db2 {main unlocked temp closed file2 reserved} 263a6abd041Sdrh 2643a81de11Sdanielk1977do_test attach2-4.7 { 2653a81de11Sdanielk1977 # Ensure handle 'db' retains the lock on the main file after 2662ac3ee97Sdrh # failing to obtain a write-lock on file2. 2672ac3ee97Sdrh catchsql { 2683a81de11Sdanielk1977 INSERT INTO t1 VALUES(1, 2) 2693a81de11Sdanielk1977 } db2 270a6abd041Sdrh} {0 {}} 271a6abd041Sdrh 27234f4732bSdrhlock_status 4.7.1 db {main shared temp closed file2 shared} 27334f4732bSdrhlock_status 4.7.2 db2 {main reserved temp closed file2 reserved} 274a6abd041Sdrh 2753a81de11Sdanielk1977do_test attach2-4.8 { 276a6abd041Sdrh # We should still be able to read test.db from db2 2773a81de11Sdanielk1977 execsql {SELECT * FROM t1} db2 278a6abd041Sdrh} {1 2} 279a6abd041Sdrh 28034f4732bSdrhlock_status 4.8.1 db {main shared temp closed file2 shared} 28134f4732bSdrhlock_status 4.8.2 db2 {main reserved temp closed file2 reserved} 282a6abd041Sdrh 2833a81de11Sdanielk1977do_test attach2-4.9 { 2843a81de11Sdanielk1977 # Try to upgrade the handle 'db' lock. 2852ac3ee97Sdrh catchsql { 2863a81de11Sdanielk1977 INSERT INTO t1 VALUES(1, 2) 2873a81de11Sdanielk1977 } 2883a81de11Sdanielk1977} {1 {database is locked}} 289a6abd041Sdrh 29034f4732bSdrhlock_status 4.9.1 db {main shared temp closed file2 shared} 29134f4732bSdrhlock_status 4.9.2 db2 {main reserved temp closed file2 reserved} 292a6abd041Sdrh 2933a81de11Sdanielk1977do_test attach2-4.10 { 294ff13c7d6Sdrh # We cannot commit db2 while db is holding a read-lock 295ff13c7d6Sdrh catchsql {COMMIT} db2 296ff13c7d6Sdrh} {1 {database is locked}} 297a6abd041Sdrh 29834f4732bSdrhlock_status 4.10.1 db {main shared temp closed file2 shared} 29934f4732bSdrhlock_status 4.10.2 db2 {main pending temp closed file2 reserved} 300a6abd041Sdrh 3013cde3bb0Sdrhset sqlite_os_trace 0 3023a81de11Sdanielk1977do_test attach2-4.11 { 303ff13c7d6Sdrh # db is able to commit. 304ff13c7d6Sdrh catchsql {COMMIT} 305ff13c7d6Sdrh} {0 {}} 306ff13c7d6Sdrh 30734f4732bSdrhlock_status 4.11.1 db {main unlocked temp closed file2 unlocked} 30834f4732bSdrhlock_status 4.11.2 db2 {main pending temp closed file2 reserved} 309ff13c7d6Sdrh 310ff13c7d6Sdrhdo_test attach2-4.12 { 311ff13c7d6Sdrh # Now we can commit db2 312ff13c7d6Sdrh catchsql {COMMIT} db2 313ff13c7d6Sdrh} {0 {}} 314ff13c7d6Sdrh 31534f4732bSdrhlock_status 4.12.1 db {main unlocked temp closed file2 unlocked} 31634f4732bSdrhlock_status 4.12.2 db2 {main unlocked temp closed file2 unlocked} 317ff13c7d6Sdrh 318ff13c7d6Sdrhdo_test attach2-4.13 { 3193a81de11Sdanielk1977 execsql {SELECT * FROM file2.t1} 3203a81de11Sdanielk1977} {1 2} 321ff13c7d6Sdrhdo_test attach2-4.14 { 3223a81de11Sdanielk1977 execsql {INSERT INTO t1 VALUES(1, 2)} 3233a81de11Sdanielk1977} {} 324ff13c7d6Sdrhdo_test attach2-4.15 { 3253a81de11Sdanielk1977 execsql {SELECT * FROM t1} db2 326ff13c7d6Sdrh} {1 2 1 2} 3273a81de11Sdanielk1977 3283a81de11Sdanielk1977db close 3293a81de11Sdanielk1977db2 close 330fda06befSmistachkinforcedelete test2.db 331b771228eSdrhsqlite3_soft_heap_limit $soft_limit 3323c8bf55aSdrh 333962398d3Sdanielk1977# These tests - attach2-5.* - check that the master journal file is deleted 334962398d3Sdanielk1977# correctly when a multi-file transaction is committed or rolled back. 335962398d3Sdanielk1977# 336962398d3Sdanielk1977# Update: It's not actually created if a rollback occurs, so that test 337962398d3Sdanielk1977# doesn't really prove too much. 338fda06befSmistachkinforeach f [glob test.db*] {forcedelete $f} 339962398d3Sdanielk1977do_test attach2-5.1 { 340ef4ac8f9Sdrh sqlite3 db test.db 341962398d3Sdanielk1977 execsql { 342962398d3Sdanielk1977 ATTACH 'test.db2' AS aux; 343962398d3Sdanielk1977 } 344962398d3Sdanielk1977} {} 345962398d3Sdanielk1977do_test attach2-5.2 { 346962398d3Sdanielk1977 execsql { 347962398d3Sdanielk1977 BEGIN; 348962398d3Sdanielk1977 CREATE TABLE tbl(a, b, c); 349962398d3Sdanielk1977 CREATE TABLE aux.tbl(a, b, c); 350962398d3Sdanielk1977 COMMIT; 351962398d3Sdanielk1977 } 352962398d3Sdanielk1977} {} 353962398d3Sdanielk1977do_test attach2-5.3 { 35457790b8fSdrh lsort [glob test.db*] 355962398d3Sdanielk1977} {test.db test.db2} 356962398d3Sdanielk1977do_test attach2-5.4 { 357962398d3Sdanielk1977 execsql { 358962398d3Sdanielk1977 BEGIN; 359962398d3Sdanielk1977 DROP TABLE aux.tbl; 360962398d3Sdanielk1977 DROP TABLE tbl; 361962398d3Sdanielk1977 ROLLBACK; 362962398d3Sdanielk1977 } 363962398d3Sdanielk1977} {} 364962398d3Sdanielk1977do_test attach2-5.5 { 36557790b8fSdrh lsort [glob test.db*] 366962398d3Sdanielk1977} {test.db test.db2} 367962398d3Sdanielk1977 36892f9a1bbSdanielk1977# Check that a database cannot be ATTACHed or DETACHed during a transaction. 36992f9a1bbSdanielk1977do_test attach2-6.1 { 37092f9a1bbSdanielk1977 execsql { 37192f9a1bbSdanielk1977 BEGIN; 37292f9a1bbSdanielk1977 } 37392f9a1bbSdanielk1977} {} 37492f9a1bbSdanielk1977do_test attach2-6.2 { 37592f9a1bbSdanielk1977 catchsql { 37692f9a1bbSdanielk1977 ATTACH 'test3.db' as aux2; 377cf201488Sdrh DETACH aux2; 37892f9a1bbSdanielk1977 } 379cf201488Sdrh} {0 {}} 38092f9a1bbSdanielk1977 381*c6aa3815Sdrh# As of version 3.21.0: it is ok to DETACH from within a transaction 382cf9fca46Sdrh# 38392f9a1bbSdanielk1977do_test attach2-6.3 { 38492f9a1bbSdanielk1977 catchsql { 38592f9a1bbSdanielk1977 DETACH aux; 38692f9a1bbSdanielk1977 } 387cf201488Sdrh} {0 {}} 38892f9a1bbSdanielk1977 389962398d3Sdanielk1977db close 390962398d3Sdanielk1977 3913c8bf55aSdrhfinish_test 392