000001  # 2003 July 1
000002  #
000003  # The author disclaims copyright to this source code.  In place of
000004  # a legal notice, here is a blessing:
000005  #
000006  #    May you do good and not evil.
000007  #    May you find forgiveness for yourself and forgive others.
000008  #    May you share freely, never taking more than you give.
000009  #
000010  #***********************************************************************
000011  # This file implements regression tests for SQLite library.  The
000012  # focus of this script is testing the ATTACH and DETACH commands
000013  # and related functionality.
000014  #
000015  # $Id: attach2.test,v 1.38 2007/12/13 21:54:11 drh Exp $
000016  #
000017  
000018  set testdir [file dirname $argv0]
000019  source $testdir/tester.tcl
000020  
000021  ifcapable !attach {
000022    finish_test
000023    return
000024  }
000025  
000026  # Ticket #354
000027  #
000028  # Databases test.db and test2.db contain identical schemas.  Make
000029  # sure we can attach test2.db from test.db.
000030  #
000031  do_test attach2-1.1 {
000032    db eval {
000033      CREATE TABLE t1(a,b);
000034      CREATE INDEX x1 ON t1(a);
000035    }
000036    forcedelete test2.db
000037    forcedelete test2.db-journal
000038    sqlite3 db2 test2.db
000039    db2 eval {
000040      CREATE TABLE t1(a,b);
000041      CREATE INDEX x1 ON t1(a);
000042    }
000043    catchsql {
000044      ATTACH 'test2.db' AS t2;
000045    }
000046  } {0 {}}
000047  
000048  # Ticket #514
000049  #
000050  proc db_list {db} {
000051    set list {}
000052    foreach {idx name file} [execsql {PRAGMA database_list} $db] {
000053      lappend list $idx $name
000054    }
000055    return $list
000056  }
000057  db eval {DETACH t2}
000058  do_test attach2-2.1 {
000059    # lock test2.db then try to attach it.  This is no longer an error because
000060    # db2 just RESERVES the database.  It does not obtain a write-lock until
000061    # we COMMIT.
000062    db2 eval {BEGIN}
000063    db2 eval {UPDATE t1 SET a = 0 WHERE 0}
000064    catchsql {
000065      ATTACH 'test2.db' AS t2;
000066    }
000067  } {0 {}}
000068  ifcapable schema_pragmas {
000069  do_test attach2-2.2 {
000070    # make sure test2.db did get attached.
000071    db_list db
000072  } {0 main 2 t2}
000073  } ;# ifcapable schema_pragmas
000074  db2 eval {COMMIT}
000075  
000076  do_test attach2-2.5 {
000077    # Make sure we can read test2.db from db
000078    catchsql {
000079      SELECT name FROM t2.sqlite_master;
000080    }
000081  } {0 {t1 x1}}
000082  do_test attach2-2.6 {
000083    # lock test2.db and try to read from it.  This should still work because
000084    # the lock is only a RESERVED lock which does not prevent reading.
000085    #
000086    db2 eval BEGIN
000087    db2 eval {UPDATE t1 SET a = 0 WHERE 0}
000088    catchsql {
000089      SELECT name FROM t2.sqlite_master;
000090    }
000091  } {0 {t1 x1}}
000092  do_test attach2-2.7 {
000093    # but we can still read from test1.db even though test2.db is locked.
000094    catchsql {
000095      SELECT name FROM main.sqlite_master;
000096    }
000097  } {0 {t1 x1}}
000098  do_test attach2-2.8 {
000099    # start a transaction on test.db even though test2.db is locked.
000100    catchsql {
000101      BEGIN;
000102      INSERT INTO t1 VALUES(8,9);
000103    }
000104  } {0 {}}
000105  do_test attach2-2.9 {
000106    execsql {
000107      SELECT * FROM t1
000108    }
000109  } {8 9}
000110  do_test attach2-2.10 {
000111    # now try to write to test2.db.  the write should fail
000112    catchsql {
000113      INSERT INTO t2.t1 VALUES(1,2);
000114    }
000115  } {1 {database is locked}}
000116  do_test attach2-2.11 {
000117    # when the write failed in the previous test, the transaction should
000118    # have rolled back.
000119    # 
000120    # Update for version 3: A transaction is no longer rolled back if a
000121    #                       database is found to be busy.
000122    execsql {rollback}
000123    db2 eval ROLLBACK
000124    execsql {
000125      SELECT * FROM t1
000126    }
000127  } {}
000128  do_test attach2-2.12 {
000129    catchsql {
000130      COMMIT
000131    }
000132  } {1 {cannot commit - no transaction is active}}
000133  
000134  # Ticket #574:  Make sure it works using the non-callback API
000135  #
000136  do_test attach2-3.1 {
000137    set DB [sqlite3_connection_pointer db]
000138    set rc [catch {sqlite3_prepare $DB "ATTACH 'test2.db' AS t2" -1 TAIL} VM]
000139    if {$rc} {lappend rc $VM}
000140    sqlite3_step $VM
000141    sqlite3_finalize $VM
000142    set rc
000143  } {0}
000144  do_test attach2-3.2 {
000145    set rc [catch {sqlite3_prepare $DB "DETACH t2" -1 TAIL} VM]
000146    if {$rc} {lappend rc $VM}
000147    sqlite3_step $VM
000148    sqlite3_finalize $VM
000149    set rc
000150  } {0}
000151  
000152  db close
000153  for {set i 2} {$i<=15} {incr i} {
000154    catch {db$i close}
000155  }
000156  
000157  # A procedure to verify the status of locks on a database.
000158  #
000159  proc lock_status {testnum db expected_result} {
000160    # If the database was compiled with OMIT_TEMPDB set, then 
000161    # the lock_status list will not contain an entry for the temp
000162    # db. But the test code doesn't know this, so its easiest 
000163    # to filter it out of the $expected_result list here.
000164    ifcapable !tempdb {
000165      set expected_result [concat \
000166          [lrange $expected_result 0 1] \
000167          [lrange $expected_result 4 end] \
000168      ]
000169    }
000170    do_test attach2-$testnum [subst {
000171      $db cache flush  ;# The lock_status pragma should not be cached
000172      execsql {PRAGMA lock_status} $db
000173    }] $expected_result
000174  }
000175  set sqlite_os_trace 0
000176  
000177  # Tests attach2-4.* test that read-locks work correctly with attached
000178  # databases.
000179  do_test attach2-4.1 {
000180    sqlite3 db test.db
000181    sqlite3 db2 test.db
000182    execsql {ATTACH 'test2.db' as file2}
000183    execsql {ATTACH 'test2.db' as file2} db2
000184  } {}
000185  
000186  lock_status 4.1.1 db {main unlocked temp closed file2 unlocked}
000187  lock_status 4.1.2 db2 {main unlocked temp closed file2 unlocked}
000188  
000189  do_test attach2-4.2 {
000190    # Handle 'db' read-locks test.db
000191    execsql {BEGIN}
000192    execsql {SELECT * FROM t1}
000193    # Lock status:
000194    #    db  - shared(main)
000195    #    db2 -
000196  } {}
000197  
000198  lock_status 4.2.1 db {main shared temp closed file2 unlocked}
000199  lock_status 4.2.2 db2 {main unlocked temp closed file2 unlocked}
000200  
000201  do_test attach2-4.3 {
000202    # The read lock held by db does not prevent db2 from reading test.db
000203    execsql {SELECT * FROM t1} db2
000204  } {}
000205  
000206  lock_status 4.3.1 db {main shared temp closed file2 unlocked}
000207  lock_status 4.3.2 db2 {main unlocked temp closed file2 unlocked}
000208  
000209  do_test attach2-4.4 {
000210    # db is holding a read lock on test.db, so we should not be able
000211    # to commit a write to test.db from db2
000212    catchsql {
000213      INSERT INTO t1 VALUES(1, 2)
000214    } db2 
000215  } {1 {database is locked}}
000216  
000217  lock_status 4.4.1 db {main shared temp closed file2 unlocked}
000218  lock_status 4.4.2 db2 {main unlocked temp closed file2 unlocked}
000219  
000220  # We have to make sure that the cache_size and the soft_heap_limit
000221  # are large enough to hold the entire change in memory.  If either
000222  # is set too small, then changes will spill to the database, forcing
000223  # a reserved lock to promote to exclusive.  That will mess up our
000224  # test results. 
000225  
000226  set soft_limit [sqlite3_soft_heap_limit 0]
000227  
000228  
000229  do_test attach2-4.5 {
000230    # Handle 'db2' reserves file2.
000231    execsql {BEGIN} db2
000232    execsql {INSERT INTO file2.t1 VALUES(1, 2)} db2
000233    # Lock status:
000234    #    db  - shared(main)
000235    #    db2 - reserved(file2)
000236  } {}
000237  
000238  lock_status 4.5.1 db {main shared temp closed file2 unlocked}
000239  lock_status 4.5.2 db2 {main unlocked temp closed file2 reserved}
000240  
000241  do_test attach2-4.6.1 {
000242    # Reads are allowed against a reserved database.
000243    catchsql {
000244      SELECT * FROM file2.t1;
000245    }
000246    # Lock status:
000247    #    db  - shared(main), shared(file2)
000248    #    db2 - reserved(file2)
000249  } {0 {}}
000250  
000251  lock_status 4.6.1.1 db {main shared temp closed file2 shared}
000252  lock_status 4.6.1.2 db2 {main unlocked temp closed file2 reserved}
000253  
000254  do_test attach2-4.6.2 {
000255    # Writes against a reserved database are not allowed.
000256    catchsql {
000257      UPDATE file2.t1 SET a=0;
000258    }
000259  } {1 {database is locked}}
000260  
000261  lock_status 4.6.2.1 db {main shared temp closed file2 shared}
000262  lock_status 4.6.2.2 db2 {main unlocked temp closed file2 reserved}
000263  
000264  do_test attach2-4.7 {
000265    # Ensure handle 'db' retains the lock on the main file after
000266    # failing to obtain a write-lock on file2.
000267    catchsql {
000268      INSERT INTO t1 VALUES(1, 2)
000269    } db2 
000270  } {0 {}}
000271  
000272  lock_status 4.7.1 db {main shared temp closed file2 shared}
000273  lock_status 4.7.2 db2 {main reserved temp closed file2 reserved}
000274  
000275  do_test attach2-4.8 {
000276    # We should still be able to read test.db from db2
000277    execsql {SELECT * FROM t1} db2
000278  } {1 2}
000279  
000280  lock_status 4.8.1 db {main shared temp closed file2 shared}
000281  lock_status 4.8.2 db2 {main reserved temp closed file2 reserved}
000282  
000283  do_test attach2-4.9 {
000284    # Try to upgrade the handle 'db' lock.
000285    catchsql {
000286      INSERT INTO t1 VALUES(1, 2)
000287    }
000288  } {1 {database is locked}}
000289  
000290  lock_status 4.9.1 db {main shared temp closed file2 shared}
000291  lock_status 4.9.2 db2 {main reserved temp closed file2 reserved}
000292  
000293  do_test attach2-4.10 {
000294    # We cannot commit db2 while db is holding a read-lock
000295    catchsql {COMMIT} db2
000296  } {1 {database is locked}}
000297  
000298  lock_status 4.10.1 db {main shared temp closed file2 shared}
000299  lock_status 4.10.2 db2 {main pending temp closed file2 reserved}
000300  
000301  set sqlite_os_trace 0
000302  do_test attach2-4.11 {
000303    # db is able to commit.
000304    catchsql {COMMIT}
000305  } {0 {}}
000306  
000307  lock_status 4.11.1 db {main unlocked temp closed file2 unlocked}
000308  lock_status 4.11.2 db2 {main pending temp closed file2 reserved}
000309  
000310  do_test attach2-4.12 {
000311    # Now we can commit db2
000312    catchsql {COMMIT} db2
000313  } {0 {}}
000314  
000315  lock_status 4.12.1 db {main unlocked temp closed file2 unlocked}
000316  lock_status 4.12.2 db2 {main unlocked temp closed file2 unlocked}
000317  
000318  do_test attach2-4.13 {
000319    execsql {SELECT * FROM file2.t1}
000320  } {1 2}
000321  do_test attach2-4.14 {
000322    execsql {INSERT INTO t1 VALUES(1, 2)}
000323  } {}
000324  do_test attach2-4.15 {
000325    execsql {SELECT * FROM t1} db2
000326  } {1 2 1 2}
000327  
000328  db close
000329  db2 close
000330  forcedelete test2.db
000331  sqlite3_soft_heap_limit $soft_limit
000332  
000333  # These tests - attach2-5.* - check that the master journal file is deleted
000334  # correctly when a multi-file transaction is committed or rolled back.
000335  #
000336  # Update: It's not actually created if a rollback occurs, so that test
000337  # doesn't really prove too much.
000338  foreach f [glob test.db*] {forcedelete $f}
000339  do_test attach2-5.1 {
000340    sqlite3 db test.db
000341    execsql {
000342      ATTACH 'test.db2' AS aux;
000343    }
000344  } {}
000345  do_test attach2-5.2 {
000346    execsql {
000347      BEGIN;
000348      CREATE TABLE tbl(a, b, c);
000349      CREATE TABLE aux.tbl(a, b, c);
000350      COMMIT;
000351    }
000352  } {}
000353  do_test attach2-5.3 {
000354    lsort [glob test.db*]
000355  } {test.db test.db2}
000356  do_test attach2-5.4 {
000357    execsql {
000358      BEGIN;
000359      DROP TABLE aux.tbl;
000360      DROP TABLE tbl;
000361      ROLLBACK;
000362    }
000363  } {}
000364  do_test attach2-5.5 {
000365    lsort [glob test.db*]
000366  } {test.db test.db2}
000367  
000368  # Check that a database cannot be ATTACHed or DETACHed during a transaction.
000369  do_test attach2-6.1 {
000370    execsql {
000371      BEGIN;
000372    }
000373  } {}
000374  do_test attach2-6.2 {
000375    catchsql {
000376      ATTACH 'test3.db' as aux2;
000377    }
000378  } {1 {cannot ATTACH database within transaction}}
000379  
000380  # EVIDENCE-OF: R-59740-55581 This statement will fail if SQLite is in
000381  # the middle of a transaction.
000382  #
000383  do_test attach2-6.3 {
000384    catchsql {
000385      DETACH aux;
000386    }
000387  } {1 {cannot DETACH database within transaction}}
000388  do_test attach2-6.4 {
000389    execsql {
000390      COMMIT;
000391      DETACH aux;
000392    }
000393  } {}
000394  
000395  db close
000396  
000397  finish_test