325 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			325 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| # 2007 March 24
 | |
| #
 | |
| # The author disclaims copyright to this source code.  In place of
 | |
| # a legal notice, here is a blessing:
 | |
| #
 | |
| #    May you do good and not evil.
 | |
| #    May you find forgiveness for yourself and forgive others.
 | |
| #    May you share freely, never taking more than you give.
 | |
| #
 | |
| #***********************************************************************
 | |
| # This file implements regression tests for SQLite library.
 | |
| #
 | |
| # $Id: exclusive2.test,v 1.10 2008/11/27 02:22:11 drh Exp $
 | |
| 
 | |
| set testdir [file dirname $argv0]
 | |
| source $testdir/tester.tcl
 | |
| 
 | |
| # Do not use a codec for tests in this file, as the database file is
 | |
| # manipulated directly using tcl scripts (using the [hexio_write] command).
 | |
| #
 | |
| do_not_use_codec
 | |
| 
 | |
| ifcapable {!pager_pragmas} {
 | |
|   finish_test
 | |
|   return
 | |
| }
 | |
| 
 | |
| # Tests in this file verify that locking_mode=exclusive causes SQLite to
 | |
| # use cached pages even if the database is changed on disk. This doesn't
 | |
| # work with mmap.
 | |
| if {[permutation]=="mmap"} {
 | |
|   finish_test
 | |
|   return
 | |
| }
 | |
| 
 | |
| # This module does not work right if the cache spills at unexpected
 | |
| # moments.  So disable the soft-heap-limit.
 | |
| #
 | |
| sqlite3_soft_heap_limit 0
 | |
| 
 | |
| proc pagerChangeCounter {filename new {fd ""}} {
 | |
|   if {$fd==""} {
 | |
|     set fd [open $filename RDWR]
 | |
|     fconfigure $fd -translation binary -encoding binary
 | |
|     set needClose 1
 | |
|   } else {
 | |
|     set needClose 0
 | |
|   }
 | |
|   if {$new ne ""} {
 | |
|     seek $fd 24
 | |
|     set a [expr {($new&0xFF000000)>>24}]
 | |
|     set b [expr {($new&0x00FF0000)>>16}]
 | |
|     set c [expr {($new&0x0000FF00)>>8}]
 | |
|     set d [expr {($new&0x000000FF)}]
 | |
|     puts -nonewline $fd [binary format cccc $a $b $c $d]
 | |
|     flush $fd
 | |
|   }
 | |
| 
 | |
|   seek $fd 24
 | |
|   foreach {a b c d} [list 0 0 0 0] {}
 | |
|   binary scan [read $fd 4] cccc a b c d
 | |
|   set  ret [expr ($a&0x000000FF)<<24]
 | |
|   incr ret [expr ($b&0x000000FF)<<16]
 | |
|   incr ret [expr ($c&0x000000FF)<<8]
 | |
|   incr ret [expr ($d&0x000000FF)<<0]
 | |
| 
 | |
|   if {$needClose} {close $fd}
 | |
|   return $ret
 | |
| }
 | |
| 
 | |
| proc readPagerChangeCounter {filename} {
 | |
|   set fd [open $filename RDONLY]
 | |
|   fconfigure $fd -translation binary -encoding binary
 | |
| 
 | |
|   seek $fd 24
 | |
|   foreach {a b c d} [list 0 0 0 0] {}
 | |
|   binary scan [read $fd 4] cccc a b c d
 | |
|   set  ret [expr ($a&0x000000FF)<<24]
 | |
|   incr ret [expr ($b&0x000000FF)<<16]
 | |
|   incr ret [expr ($c&0x000000FF)<<8]
 | |
|   incr ret [expr ($d&0x000000FF)<<0]
 | |
| 
 | |
|   close $fd
 | |
|   return $ret
 | |
| }
 | |
| 
 | |
| 
 | |
| proc t1sig {{db db}} {
 | |
|   execsql {SELECT count(*), md5sum(a) FROM t1} $db
 | |
| }
 | |
| do_test exclusive2-1.0 {
 | |
|   readPagerChangeCounter test.db
 | |
| } {0}
 | |
| 
 | |
| #-----------------------------------------------------------------------
 | |
| # The following tests - exclusive2-1.X - check that:
 | |
| #
 | |
| # 1-3:   Build a database with connection 1, calculate a signature.
 | |
| # 4-7:   Modify the database using a second connection in a way that
 | |
| #        does not modify the freelist, then reset the pager change-counter
 | |
| #        to the value it had before the modifications.
 | |
| # 8:     Check that using the first connection, the database signature
 | |
| #        is still the same. This is because it uses the in-memory cache.
 | |
| #        It can't tell the db has changed because we reset the change-counter.
 | |
| # 9:     Increment the change-counter.
 | |
| # 10:    Ensure that the first connection now sees the updated database. It
 | |
| #        sees the change-counter has been incremented and discards the 
 | |
| #        invalid in-memory cache.
 | |
| #
 | |
| # This will only work if the database cache is large enough to hold 
 | |
| # the entire database. In the case of 1024 byte pages, this means
 | |
| # the cache size must be at least 17. Otherwise, some pages will be
 | |
| # loaded from the database file in step 8.
 | |
| #
 | |
| # For similar reasons, this test does not work with the memsubsys1 permutation.
 | |
| # Permutation memsubsys1 configures the pcache subsystem to use a static
 | |
| # allocation of 24 pages (shared between all pagers). This is not enough for
 | |
| # this test.
 | |
| #
 | |
| do_test exclusive2-1.1 {
 | |
|   execsql {
 | |
|     BEGIN;
 | |
|     CREATE TABLE t1(a, b);
 | |
|     INSERT INTO t1(a) VALUES(randstr(10, 400));
 | |
|     INSERT INTO t1(a) VALUES(randstr(10, 400));
 | |
|     INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
 | |
|     INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
 | |
|     INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
 | |
|     INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
 | |
|     INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
 | |
|     COMMIT;
 | |
|     SELECT count(*) FROM t1;
 | |
|   }
 | |
| } {64}
 | |
| do_test exclusive2-1.2.1 {
 | |
|   # Make sure the pager cache is large enough to store the 
 | |
|   # entire database.
 | |
|   set nPage [expr [file size test.db]/1024]
 | |
|   if {$::SQLITE_DEFAULT_CACHE_SIZE < $nPage} {
 | |
|     execsql "PRAGMA cache_size = $nPage"
 | |
|   }
 | |
|   expr {[execsql {PRAGMA cache_size}] >= $nPage}
 | |
| } {1}
 | |
| do_test exclusive2-1.2 {
 | |
|   set ::sig [t1sig]
 | |
|   readPagerChangeCounter test.db
 | |
| } {1}
 | |
| do_test exclusive2-1.3 {
 | |
|   t1sig
 | |
| } $::sig
 | |
| do_test exclusive2-1.4 {
 | |
|   sqlite3 db2 test.db
 | |
|   t1sig db2
 | |
| } $::sig
 | |
| do_test exclusive2-1.5 {
 | |
|   execsql {
 | |
|     UPDATE t1 SET b=a, a=NULL;
 | |
|   } db2
 | |
|   expr {[t1sig db2] eq $::sig}
 | |
| } 0
 | |
| do_test exclusive2-1.6 {
 | |
|   readPagerChangeCounter test.db
 | |
| } {2}
 | |
| do_test exclusive2-1.7 {
 | |
|   pagerChangeCounter test.db 1
 | |
| } {1}
 | |
| if {[permutation] != "memsubsys1"} {
 | |
|   do_test exclusive2-1.9 {
 | |
|     t1sig
 | |
|     expr {[t1sig] eq $::sig}
 | |
|   } {1}
 | |
| }
 | |
| do_test exclusive2-1.10 {
 | |
|   pagerChangeCounter test.db 2
 | |
| } {2}
 | |
| do_test exclusive2-1.11 {
 | |
|   expr {[t1sig] eq $::sig}
 | |
| } {0}
 | |
| db2 close
 | |
| 
 | |
| #--------------------------------------------------------------------
 | |
| # These tests - exclusive2-2.X - are similar to exclusive2-1.X, 
 | |
| # except that they are run with locking_mode=EXCLUSIVE.
 | |
| #
 | |
| # 1-3:   Build a database with exclusive-access connection 1, 
 | |
| #        calculate a signature.
 | |
| # 4:     Corrupt the database by writing 10000 bytes of garbage
 | |
| #        starting at the beginning of page 2. Check that connection 1
 | |
| #        still works. It should be accessing the in-memory cache.
 | |
| # 5-6:   Modify the dataase change-counter. Connection 1 still works
 | |
| #        entirely from in-memory cache, because it doesn't check the
 | |
| #        change-counter.
 | |
| # 7-8    Set the locking-mode back to normal. After the db is unlocked,
 | |
| #        SQLite detects the modified change-counter and discards the
 | |
| #        in-memory cache. Then it finds the corruption caused in step 4....
 | |
| #
 | |
| # As above, this test is only applicable if the pager cache is
 | |
| # large enough to hold the entire database. With 1024 byte pages,
 | |
| # this means 19 pages.  We also need to disable the soft-heap-limit
 | |
| # to prevent memory-induced cache spills.
 | |
| #
 | |
| do_test exclusive2-2.1 {
 | |
|   execsql {PRAGMA cache_size=1000;}
 | |
|   execsql {PRAGMA locking_mode = exclusive;}
 | |
|   execsql {
 | |
|     BEGIN;
 | |
|     DELETE FROM t1;
 | |
|     INSERT INTO t1(a) VALUES(randstr(10, 400));
 | |
|     INSERT INTO t1(a) VALUES(randstr(10, 400));
 | |
|     INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
 | |
|     INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
 | |
|     INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
 | |
|     INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
 | |
|     INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
 | |
|     COMMIT;
 | |
|     SELECT count(*) FROM t1;
 | |
|   }
 | |
| } {64}
 | |
| do_test exclusive2-2.2.1 {
 | |
|   # Make sure the pager cache is large enough to store the 
 | |
|   # entire database.
 | |
|   set nPage [expr [file size test.db]/1024]
 | |
|   if {$::SQLITE_DEFAULT_CACHE_SIZE < $nPage} {
 | |
|     execsql "PRAGMA cache_size = $nPage"
 | |
|   }
 | |
|   expr {[execsql {PRAGMA cache_size}] >= $nPage}
 | |
| } {1}
 | |
| do_test exclusive2-2.2 {
 | |
|   set ::sig [t1sig]
 | |
|   readPagerChangeCounter test.db
 | |
| } {3}
 | |
| do_test exclusive2-2.3 {
 | |
|   t1sig
 | |
| } $::sig
 | |
| 
 | |
| do_test exclusive2-2.4 {
 | |
|   set ::fd [open test.db RDWR]
 | |
|   fconfigure $::fd -translation binary
 | |
|   seek $::fd 1024
 | |
|   puts -nonewline $::fd [string repeat [binary format c 0] 10000]
 | |
|   flush $::fd
 | |
|   t1sig
 | |
| } $::sig
 | |
| 
 | |
| do_test exclusive2-2.5 {
 | |
|   pagerChangeCounter test.db 5 $::fd
 | |
| } {5}
 | |
| do_test exclusive2-2.6 {
 | |
|   t1sig
 | |
| } $::sig
 | |
| do_test exclusive2-2.7 {
 | |
|   execsql {PRAGMA locking_mode = normal}
 | |
|   t1sig
 | |
| } $::sig
 | |
| 
 | |
| do_test exclusive2-2.8 {
 | |
|   set rc [catch {t1sig} msg]
 | |
|   list $rc $msg
 | |
| } {1 {database disk image is malformed}}
 | |
| 
 | |
| #--------------------------------------------------------------------
 | |
| # These tests - exclusive2-3.X - verify that the pager change-counter
 | |
| # is only incremented by the first change when in exclusive access
 | |
| # mode. In normal mode, the change-counter is incremented once
 | |
| # per write-transaction.
 | |
| #
 | |
| 
 | |
| db close
 | |
| catch {close $::fd}
 | |
| forcedelete test.db
 | |
| forcedelete test.db-journal
 | |
| 
 | |
| do_test exclusive2-3.0 {
 | |
|   sqlite3 db test.db
 | |
|   execsql {
 | |
|     BEGIN;
 | |
|     CREATE TABLE t1(a UNIQUE);
 | |
|     INSERT INTO t1 VALUES(randstr(200, 200));
 | |
|     INSERT INTO t1 VALUES(randstr(200, 200));
 | |
|     COMMIT;
 | |
|   }
 | |
|   readPagerChangeCounter test.db
 | |
| } {1}
 | |
| do_test exclusive2-3.1 {
 | |
|   execsql {
 | |
|     INSERT INTO t1 VALUES(randstr(200, 200));
 | |
|   }
 | |
|   readPagerChangeCounter test.db
 | |
| } {2}
 | |
| do_test exclusive2-3.2 {
 | |
|   execsql {
 | |
|     INSERT INTO t1 VALUES(randstr(200, 200));
 | |
|   }
 | |
|   readPagerChangeCounter test.db
 | |
| } {3}
 | |
| do_test exclusive2-3.3 {
 | |
|   execsql {
 | |
|     PRAGMA locking_mode = exclusive;
 | |
|     INSERT INTO t1 VALUES(randstr(200, 200));
 | |
|   }
 | |
|   readPagerChangeCounter test.db
 | |
| } {4}
 | |
| do_test exclusive2-3.4 {
 | |
|   execsql {
 | |
|     INSERT INTO t1 VALUES(randstr(200, 200));
 | |
|   }
 | |
|   readPagerChangeCounter test.db
 | |
| } {4}
 | |
| do_test exclusive2-3.5 {
 | |
|   execsql {
 | |
|     PRAGMA locking_mode = normal;
 | |
|     INSERT INTO t1 VALUES(randstr(200, 200));
 | |
|   }
 | |
|   readPagerChangeCounter test.db
 | |
| } {4}
 | |
| do_test exclusive2-3.6 {
 | |
|   execsql {
 | |
|     INSERT INTO t1 VALUES(randstr(200, 200));
 | |
|   }
 | |
|   readPagerChangeCounter test.db
 | |
| } {5}
 | |
| sqlite3_soft_heap_limit $cmdlinearg(soft-heap-limit)
 | |
| 
 | |
| finish_test
 | 
