Home | History | Annotate | Download | only in test
      1 # 2010 November 19
      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 #
     12 
     13 set testdir [file dirname $argv0]
     14 source $testdir/tester.tcl
     15 source $testdir/lock_common.tcl
     16 
     17 set testprefix superlock
     18 
     19 # Test organization:
     20 #
     21 #   1.*: Test superlock on a rollback database. Test that once the db is
     22 #        superlocked, it is not possible for a second client to read from
     23 #        it.
     24 #
     25 #   2.*: Test superlock on a WAL database with zero frames in the WAL file.
     26 #        Test that once the db is superlocked, it is not possible to read,
     27 #        write or checkpoint the db.
     28 #
     29 #   3.*: As 2.*, for WAL databases with one or more frames in the WAL.
     30 #
     31 #   4.*: As 2.*, for WAL databases with one or more checkpointed frames 
     32 #        in the WAL.
     33 #
     34 #   5.*: Test that a call to sqlite3demo_superlock() uses the busy handler
     35 #        correctly to wait for existing clients to clear on a WAL database.
     36 #        And returns SQLITE_BUSY if no busy handler is defined or the busy
     37 #        handler returns 0 before said clients relinquish their locks.
     38 #
     39 #   6.*: Test that if a superlocked WAL database is overwritten, existing
     40 #        clients run the recovery to build the new wal-index after the 
     41 #        superlock is released.
     42 #        
     43 #
     44 
     45 do_execsql_test 1.1 {
     46   CREATE TABLE t1(a, b);
     47   INSERT INTO t1 VALUES(1, 2);
     48   PRAGMA journal_mode = DELETE;
     49 } {delete}
     50 
     51 do_test 1.2 { sqlite3demo_superlock unlock test.db } {unlock}
     52 do_catchsql_test 1.3 { SELECT * FROM t1 } {1 {database is locked}}
     53 do_test 1.4 { unlock } {}
     54 
     55 do_execsql_test 2.1 { 
     56   INSERT INTO t1 VALUES(3, 4);
     57   PRAGMA journal_mode = WAL;
     58 } {wal}
     59 
     60 do_test 2.2 { sqlite3demo_superlock unlock test.db } {unlock}
     61 do_catchsql_test 2.3 { SELECT * FROM t1 }           {1 {database is locked}}
     62 do_catchsql_test 2.4 { INSERT INTO t1 VALUES(5, 6)} {1 {database is locked}}
     63 do_catchsql_test 2.5 { PRAGMA wal_checkpoint }      {0 {1 -1 -1}}
     64 do_test 2.6 { unlock } {}
     65 
     66 do_execsql_test 3.1 { INSERT INTO t1 VALUES(3, 4) } 
     67 
     68 do_test 3.2 { sqlite3demo_superlock unlock test.db } {unlock}
     69 do_catchsql_test 3.3 { SELECT * FROM t1 }           {1 {database is locked}}
     70 do_catchsql_test 3.4 { INSERT INTO t1 VALUES(5, 6)} {1 {database is locked}}
     71 do_catchsql_test 3.5 { PRAGMA wal_checkpoint }      {0 {1 -1 -1}}
     72 do_test 3.6 { unlock } {}
     73 
     74 do_execsql_test 4.1 { PRAGMA wal_checkpoint } {0 2 2}
     75 
     76 do_test 4.2 { sqlite3demo_superlock unlock test.db } {unlock}
     77 do_catchsql_test 4.3 { SELECT * FROM t1 }           {1 {database is locked}}
     78 do_catchsql_test 4.4 { INSERT INTO t1 VALUES(5, 6)} {1 {database is locked}}
     79 do_catchsql_test 4.5 { PRAGMA wal_checkpoint }      {0 {1 -1 -1}}
     80 do_test 4.6 { unlock } {}
     81 
     82 do_multiclient_test tn {
     83 
     84   proc busyhandler {x} {
     85     switch -- $x {
     86       1 { sql1 "COMMIT" }
     87       2 { sql2 "COMMIT" }
     88       3 { sql3 "COMMIT" }
     89     }
     90     lappend ::busylist $x
     91     return 1
     92   }
     93   set ::busylist [list]
     94 
     95   do_test 5.$tn.1 {
     96     sql1 {
     97       CREATE TABLE t1(a, b);
     98       PRAGMA journal_mode = WAL;
     99       INSERT INTO t1 VALUES(1, 2);
    100     }
    101   } {wal}
    102 
    103   do_test 5.$tn.2 {
    104     sql1 { BEGIN ; SELECT * FROM t1 }
    105     sql2 { BEGIN ; INSERT INTO t1 VALUES(3, 4) }
    106     sql3 { BEGIN ; SELECT * FROM t1 }
    107   } {1 2}
    108 
    109   do_test 5.$tn.3 {
    110     set ::busylist [list]
    111     sqlite3demo_superlock unlock test.db "" busyhandler
    112     set ::busylist
    113   } {0 1 2 3}
    114 
    115   do_test 5.$tn.4 { csql2 { SELECT * FROM t1 } } {1 {database is locked}}
    116   do_test 5.$tn.5 { 
    117     csql3 { INSERT INTO t1 VALUES(5, 6) } 
    118   } {1 {database is locked}}
    119   do_test 5.$tn.6 { csql1 "PRAGMA wal_checkpoint" } {0 {1 -1 -1}}
    120 
    121   do_test 5.$tn.7 { unlock } {}
    122 
    123   
    124   do_test 5.$tn.8 {
    125     sql1 { BEGIN ; SELECT * FROM t1 }
    126     sql2 { BEGIN ; INSERT INTO t1 VALUES(5, 6) }
    127     sql3 { BEGIN ; SELECT * FROM t1 }
    128   } {1 2 3 4}
    129 
    130   do_test 5.$tn.9 { 
    131     list [catch {sqlite3demo_superlock unlock test.db} msg] $msg
    132   } {1 {database is locked}}
    133   do_test 5.$tn.10 { 
    134     sql1 COMMIT
    135     list [catch {sqlite3demo_superlock unlock test.db} msg] $msg
    136   } {1 {database is locked}}
    137   do_test 5.$tn.11 { 
    138     sql2 COMMIT
    139     list [catch {sqlite3demo_superlock unlock test.db} msg] $msg
    140   } {1 {database is locked}}
    141   do_test 5.$tn.12 { 
    142     sql3 COMMIT
    143     list [catch {sqlite3demo_superlock unlock test.db} msg] $msg
    144   } {0 unlock}
    145   unlock
    146 
    147 
    148   do_test 5.$tn.13 { sql1 { SELECT * FROM t1 } } {1 2 3 4 5 6}
    149   do_test 5.$tn.14 { sql2 { SELECT * FROM t1 } } {1 2 3 4 5 6}
    150   do_test 5.$tn.15 { sqlite3demo_superlock unlock test.db } {unlock}
    151   do_test 5.$tn.16 { unlock } {}
    152   do_test 5.$tn.17 { sql2 { SELECT * FROM t1 } } {1 2 3 4 5 6}
    153   do_test 5.$tn.18 { sql1 { SELECT * FROM t1 } } {1 2 3 4 5 6}
    154   do_test 5.$tn.19 { sql2 { SELECT * FROM t1 } } {1 2 3 4 5 6}
    155 }
    156 
    157 proc read_content {file} {
    158   if {[file exists $file]==0} {return ""}
    159   set fd [open $file]
    160   fconfigure $fd -encoding binary -translation binary
    161   set content [read $fd]
    162   close $fd
    163   return $content
    164 }
    165 
    166 proc write_content {file content} {
    167   set fd [open $file w+]
    168   fconfigure $fd -encoding binary -translation binary
    169   puts -nonewline $fd $content
    170   close $fd
    171 }
    172 
    173 # Both $file1 and $file2 are database files. This function takes a 
    174 # superlock on each, then exchanges the content of the two files (i.e.
    175 # overwrites $file1 with the initial contents of $file2, and overwrites
    176 # $file2 with the initial contents of $file1). The contents of any WAL 
    177 # file is also exchanged.
    178 #
    179 proc db_swap {file1 file2} {
    180   sqlite3demo_superlock unlock1 $file1
    181   sqlite3demo_superlock unlock2 $file2
    182 
    183   set db1 [read_content $file1]
    184   set db2 [read_content $file2]
    185   write_content $file1 $db2
    186   write_content $file2 $db1
    187 
    188   set wal1 [read_content ${file1}-wal]
    189   set wal2 [read_content ${file2}-wal]
    190   write_content ${file1}-wal $wal2
    191   write_content ${file2}-wal $wal1
    192 
    193   unlock1
    194   unlock2
    195 }
    196 
    197 forcedelete test.db
    198 sqlite3 db  test.db
    199 do_execsql_test 6.1 {
    200   ATTACH 'test.db2' AS aux;
    201   PRAGMA aux.journal_mode = wal;
    202   CREATE TABLE aux.t2(x, y);
    203   INSERT INTO aux.t2 VALUES('a', 'b');
    204   PRAGMA schema_version = 450;
    205   DETACH aux;
    206 
    207   PRAGMA main.journal_mode = wal;
    208   CREATE TABLE t1(a, b);
    209   INSERT INTO t1 VALUES(1, 2);
    210   INSERT INTO t1 VALUES(3, 4);
    211   SELECT * FROM t1;
    212 } {wal wal 1 2 3 4}
    213 
    214 
    215 db_swap test.db2 test.db
    216 do_catchsql_test 6.2 { SELECT * FROM t1 } {1 {no such table: t1}}
    217 do_catchsql_test 6.3 { SELECT * FROM t2 } {0 {a b}}
    218 
    219 db_swap test.db2 test.db
    220 do_catchsql_test 6.4 { SELECT * FROM t1 } {0 {1 2 3 4}}
    221 do_catchsql_test 6.5 { SELECT * FROM t2 } {1 {no such table: t2}}
    222 
    223 do_execsql_test  6.6 { PRAGMA wal_checkpoint } {0 0 0}
    224 
    225 db_swap test.db2 test.db
    226 do_catchsql_test 6.7 { SELECT * FROM t1 } {1 {no such table: t1}}
    227 do_catchsql_test 6.8 { SELECT * FROM t2 } {0 {a b}}
    228 
    229 db_swap test.db2 test.db
    230 do_catchsql_test 6.9 { SELECT * FROM t1 } {0 {1 2 3 4}}
    231 do_catchsql_test 6.10 { SELECT * FROM t2 } {1 {no such table: t2}}
    232 
    233 do_execsql_test  6.11 { 
    234   PRAGMA journal_mode = delete;
    235   PRAGMA page_size = 512;
    236   VACUUM;
    237   PRAGMA journal_mode = wal;
    238   INSERT INTO t1 VALUES(5, 6);
    239 } {delete wal}
    240 
    241 db_swap test.db2 test.db
    242 do_catchsql_test 6.12 { SELECT * FROM t1 } {1 {no such table: t1}}
    243 do_catchsql_test 6.13 { SELECT * FROM t2 } {0 {a b}}
    244 
    245 db_swap test.db2 test.db
    246 do_catchsql_test 6.14 { SELECT * FROM t1 } {0 {1 2 3 4 5 6}}
    247 do_catchsql_test 6.15 { SELECT * FROM t2 } {1 {no such table: t2}}
    248 
    249 finish_test
    250