Home | History | Annotate | Download | only in test
      1 # 2007 March 24
      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 # This file implements regression tests for SQLite library.
     12 #
     13 # $Id: exclusive2.test,v 1.10 2008/11/27 02:22:11 drh Exp $
     14 
     15 set testdir [file dirname $argv0]
     16 source $testdir/tester.tcl
     17 
     18 # Do not use a codec for tests in this file, as the database file is
     19 # manipulated directly using tcl scripts (using the [hexio_write] command).
     20 #
     21 do_not_use_codec
     22 
     23 ifcapable {!pager_pragmas} {
     24   finish_test
     25   return
     26 }
     27 
     28 # This module does not work right if the cache spills at unexpected
     29 # moments.  So disable the soft-heap-limit.
     30 #
     31 sqlite3_soft_heap_limit 0
     32 
     33 proc pagerChangeCounter {filename new {fd ""}} {
     34   if {$fd==""} {
     35     set fd [open $filename RDWR]
     36     fconfigure $fd -translation binary -encoding binary
     37     set needClose 1
     38   } else {
     39     set needClose 0
     40   }
     41   if {$new ne ""} {
     42     seek $fd 24
     43     set a [expr {($new&0xFF000000)>>24}]
     44     set b [expr {($new&0x00FF0000)>>16}]
     45     set c [expr {($new&0x0000FF00)>>8}]
     46     set d [expr {($new&0x000000FF)}]
     47     puts -nonewline $fd [binary format cccc $a $b $c $d]
     48     flush $fd
     49   }
     50 
     51   seek $fd 24
     52   foreach {a b c d} [list 0 0 0 0] {}
     53   binary scan [read $fd 4] cccc a b c d
     54   set  ret [expr ($a&0x000000FF)<<24]
     55   incr ret [expr ($b&0x000000FF)<<16]
     56   incr ret [expr ($c&0x000000FF)<<8]
     57   incr ret [expr ($d&0x000000FF)<<0]
     58 
     59   if {$needClose} {close $fd}
     60   return $ret
     61 }
     62 
     63 proc readPagerChangeCounter {filename} {
     64   set fd [open $filename RDONLY]
     65   fconfigure $fd -translation binary -encoding binary
     66 
     67   seek $fd 24
     68   foreach {a b c d} [list 0 0 0 0] {}
     69   binary scan [read $fd 4] cccc a b c d
     70   set  ret [expr ($a&0x000000FF)<<24]
     71   incr ret [expr ($b&0x000000FF)<<16]
     72   incr ret [expr ($c&0x000000FF)<<8]
     73   incr ret [expr ($d&0x000000FF)<<0]
     74 
     75   close $fd
     76   return $ret
     77 }
     78 
     79 
     80 proc t1sig {{db db}} {
     81   execsql {SELECT count(*), md5sum(a) FROM t1} $db
     82 }
     83 do_test exclusive2-1.0 {
     84   readPagerChangeCounter test.db
     85 } {0}
     86 
     87 #-----------------------------------------------------------------------
     88 # The following tests - exclusive2-1.X - check that:
     89 #
     90 # 1-3:   Build a database with connection 1, calculate a signature.
     91 # 4-7:   Modify the database using a second connection in a way that
     92 #        does not modify the freelist, then reset the pager change-counter
     93 #        to the value it had before the modifications.
     94 # 8:     Check that using the first connection, the database signature
     95 #        is still the same. This is because it uses the in-memory cache.
     96 #        It can't tell the db has changed because we reset the change-counter.
     97 # 9:     Increment the change-counter.
     98 # 10:    Ensure that the first connection now sees the updated database. It
     99 #        sees the change-counter has been incremented and discards the 
    100 #        invalid in-memory cache.
    101 #
    102 # This will only work if the database cache is large enough to hold 
    103 # the entire database. In the case of 1024 byte pages, this means
    104 # the cache size must be at least 17. Otherwise, some pages will be
    105 # loaded from the database file in step 8.
    106 #
    107 # For similar reasons, this test does not work with the memsubsys1 permutation.
    108 # Permutation memsubsys1 configures the pcache subsystem to use a static
    109 # allocation of 24 pages (shared between all pagers). This is not enough for
    110 # this test.
    111 #
    112 do_test exclusive2-1.1 {
    113   execsql {
    114     BEGIN;
    115     CREATE TABLE t1(a, b);
    116     INSERT INTO t1(a) VALUES(randstr(10, 400));
    117     INSERT INTO t1(a) VALUES(randstr(10, 400));
    118     INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
    119     INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
    120     INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
    121     INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
    122     INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
    123     COMMIT;
    124     SELECT count(*) FROM t1;
    125   }
    126 } {64}
    127 do_test exclusive2-1.2.1 {
    128   # Make sure the pager cache is large enough to store the 
    129   # entire database.
    130   set nPage [expr [file size test.db]/1024]
    131   if {$::SQLITE_DEFAULT_CACHE_SIZE < $nPage} {
    132     execsql "PRAGMA cache_size = $nPage"
    133   }
    134   expr {[execsql {PRAGMA cache_size}] >= $nPage}
    135 } {1}
    136 do_test exclusive2-1.2 {
    137   set ::sig [t1sig]
    138   readPagerChangeCounter test.db
    139 } {1}
    140 do_test exclusive2-1.3 {
    141   t1sig
    142 } $::sig
    143 do_test exclusive2-1.4 {
    144   sqlite3 db2 test.db
    145   t1sig db2
    146 } $::sig
    147 do_test exclusive2-1.5 {
    148   execsql {
    149     UPDATE t1 SET b=a, a=NULL;
    150   } db2
    151   expr {[t1sig db2] eq $::sig}
    152 } 0
    153 do_test exclusive2-1.6 {
    154   readPagerChangeCounter test.db
    155 } {2}
    156 do_test exclusive2-1.7 {
    157   pagerChangeCounter test.db 1
    158 } {1}
    159 if {[permutation] != "memsubsys1"} {
    160   do_test exclusive2-1.9 {
    161     t1sig
    162     expr {[t1sig] eq $::sig}
    163   } {1}
    164 }
    165 do_test exclusive2-1.10 {
    166   pagerChangeCounter test.db 2
    167 } {2}
    168 do_test exclusive2-1.11 {
    169   expr {[t1sig] eq $::sig}
    170 } {0}
    171 db2 close
    172 
    173 #--------------------------------------------------------------------
    174 # These tests - exclusive2-2.X - are similar to exclusive2-1.X, 
    175 # except that they are run with locking_mode=EXCLUSIVE.
    176 #
    177 # 1-3:   Build a database with exclusive-access connection 1, 
    178 #        calculate a signature.
    179 # 4:     Corrupt the database by writing 10000 bytes of garbage
    180 #        starting at the beginning of page 2. Check that connection 1
    181 #        still works. It should be accessing the in-memory cache.
    182 # 5-6:   Modify the dataase change-counter. Connection 1 still works
    183 #        entirely from in-memory cache, because it doesn't check the
    184 #        change-counter.
    185 # 7-8    Set the locking-mode back to normal. After the db is unlocked,
    186 #        SQLite detects the modified change-counter and discards the
    187 #        in-memory cache. Then it finds the corruption caused in step 4....
    188 #
    189 # As above, this test is only applicable if the pager cache is
    190 # large enough to hold the entire database. With 1024 byte pages,
    191 # this means 19 pages.  We also need to disable the soft-heap-limit
    192 # to prevent memory-induced cache spills.
    193 #
    194 do_test exclusive2-2.1 {
    195   execsql {PRAGMA cache_size=1000;}
    196   execsql {PRAGMA locking_mode = exclusive;}
    197   execsql {
    198     BEGIN;
    199     DELETE FROM t1;
    200     INSERT INTO t1(a) VALUES(randstr(10, 400));
    201     INSERT INTO t1(a) VALUES(randstr(10, 400));
    202     INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
    203     INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
    204     INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
    205     INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
    206     INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
    207     COMMIT;
    208     SELECT count(*) FROM t1;
    209   }
    210 } {64}
    211 do_test exclusive2-2.2.1 {
    212   # Make sure the pager cache is large enough to store the 
    213   # entire database.
    214   set nPage [expr [file size test.db]/1024]
    215   if {$::SQLITE_DEFAULT_CACHE_SIZE < $nPage} {
    216     execsql "PRAGMA cache_size = $nPage"
    217   }
    218   expr {[execsql {PRAGMA cache_size}] >= $nPage}
    219 } {1}
    220 do_test exclusive2-2.2 {
    221   set ::sig [t1sig]
    222   readPagerChangeCounter test.db
    223 } {3}
    224 do_test exclusive2-2.3 {
    225   t1sig
    226 } $::sig
    227 
    228 do_test exclusive2-2.4 {
    229   set ::fd [open test.db RDWR]
    230   fconfigure $::fd -translation binary
    231   seek $::fd 1024
    232   puts -nonewline $::fd [string repeat [binary format c 0] 10000]
    233   flush $::fd
    234   t1sig
    235 } $::sig
    236 
    237 do_test exclusive2-2.5 {
    238   pagerChangeCounter test.db 5 $::fd
    239 } {5}
    240 do_test exclusive2-2.6 {
    241   t1sig
    242 } $::sig
    243 do_test exclusive2-2.7 {
    244   execsql {PRAGMA locking_mode = normal}
    245   t1sig
    246 } $::sig
    247 
    248 do_test exclusive2-2.8 {
    249   set rc [catch {t1sig} msg]
    250   list $rc $msg
    251 } {1 {database disk image is malformed}}
    252 
    253 #--------------------------------------------------------------------
    254 # These tests - exclusive2-3.X - verify that the pager change-counter
    255 # is only incremented by the first change when in exclusive access
    256 # mode. In normal mode, the change-counter is incremented once
    257 # per write-transaction.
    258 #
    259 
    260 db close
    261 catch {close $::fd}
    262 file delete -force test.db
    263 file delete -force test.db-journal
    264 
    265 do_test exclusive2-3.0 {
    266   sqlite3 db test.db
    267   execsql {
    268     BEGIN;
    269     CREATE TABLE t1(a UNIQUE);
    270     INSERT INTO t1 VALUES(randstr(200, 200));
    271     INSERT INTO t1 VALUES(randstr(200, 200));
    272     COMMIT;
    273   }
    274   readPagerChangeCounter test.db
    275 } {1}
    276 do_test exclusive2-3.1 {
    277   execsql {
    278     INSERT INTO t1 VALUES(randstr(200, 200));
    279   }
    280   readPagerChangeCounter test.db
    281 } {2}
    282 do_test exclusive2-3.2 {
    283   execsql {
    284     INSERT INTO t1 VALUES(randstr(200, 200));
    285   }
    286   readPagerChangeCounter test.db
    287 } {3}
    288 do_test exclusive2-3.3 {
    289   execsql {
    290     PRAGMA locking_mode = exclusive;
    291     INSERT INTO t1 VALUES(randstr(200, 200));
    292   }
    293   readPagerChangeCounter test.db
    294 } {4}
    295 do_test exclusive2-3.4 {
    296 breakpoint
    297   execsql {
    298     INSERT INTO t1 VALUES(randstr(200, 200));
    299   }
    300   readPagerChangeCounter test.db
    301 } {4}
    302 do_test exclusive2-3.5 {
    303   execsql {
    304     PRAGMA locking_mode = normal;
    305     INSERT INTO t1 VALUES(randstr(200, 200));
    306   }
    307   readPagerChangeCounter test.db
    308 } {4}
    309 do_test exclusive2-3.6 {
    310   execsql {
    311     INSERT INTO t1 VALUES(randstr(200, 200));
    312   }
    313   readPagerChangeCounter test.db
    314 } {5}
    315 sqlite3_soft_heap_limit $cmdlinearg(soft-heap-limit)
    316 
    317 finish_test
    318