xref: /sqlite-3.40.0/test/attach2.test (revision c6aa3815)
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