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