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