• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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# This module does not work right if the cache spills at unexpected
29# moments.  So disable the soft-heap-limit.
30#
31sqlite3_soft_heap_limit 0
32
33proc 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
63proc 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
80proc t1sig {{db db}} {
81  execsql {SELECT count(*), md5sum(a) FROM t1} $db
82}
83do_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#
112do_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}
127do_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}
136do_test exclusive2-1.2 {
137  set ::sig [t1sig]
138  readPagerChangeCounter test.db
139} {1}
140do_test exclusive2-1.3 {
141  t1sig
142} $::sig
143do_test exclusive2-1.4 {
144  sqlite3 db2 test.db
145  t1sig db2
146} $::sig
147do_test exclusive2-1.5 {
148  execsql {
149    UPDATE t1 SET b=a, a=NULL;
150  } db2
151  expr {[t1sig db2] eq $::sig}
152} 0
153do_test exclusive2-1.6 {
154  readPagerChangeCounter test.db
155} {2}
156do_test exclusive2-1.7 {
157  pagerChangeCounter test.db 1
158} {1}
159if {[permutation] != "memsubsys1"} {
160  do_test exclusive2-1.9 {
161    t1sig
162    expr {[t1sig] eq $::sig}
163  } {1}
164}
165do_test exclusive2-1.10 {
166  pagerChangeCounter test.db 2
167} {2}
168do_test exclusive2-1.11 {
169  expr {[t1sig] eq $::sig}
170} {0}
171db2 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#
194do_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}
211do_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}
220do_test exclusive2-2.2 {
221  set ::sig [t1sig]
222  readPagerChangeCounter test.db
223} {3}
224do_test exclusive2-2.3 {
225  t1sig
226} $::sig
227
228do_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
237do_test exclusive2-2.5 {
238  pagerChangeCounter test.db 5 $::fd
239} {5}
240do_test exclusive2-2.6 {
241  t1sig
242} $::sig
243do_test exclusive2-2.7 {
244  execsql {PRAGMA locking_mode = normal}
245  t1sig
246} $::sig
247
248do_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
260db close
261catch {close $::fd}
262file delete -force test.db
263file delete -force test.db-journal
264
265do_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}
276do_test exclusive2-3.1 {
277  execsql {
278    INSERT INTO t1 VALUES(randstr(200, 200));
279  }
280  readPagerChangeCounter test.db
281} {2}
282do_test exclusive2-3.2 {
283  execsql {
284    INSERT INTO t1 VALUES(randstr(200, 200));
285  }
286  readPagerChangeCounter test.db
287} {3}
288do_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}
295do_test exclusive2-3.4 {
296breakpoint
297  execsql {
298    INSERT INTO t1 VALUES(randstr(200, 200));
299  }
300  readPagerChangeCounter test.db
301} {4}
302do_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}
309do_test exclusive2-3.6 {
310  execsql {
311    INSERT INTO t1 VALUES(randstr(200, 200));
312  }
313  readPagerChangeCounter test.db
314} {5}
315sqlite3_soft_heap_limit $cmdlinearg(soft-heap-limit)
316
317finish_test
318