wasCSharpSQLite – Blame information for rev 7
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | # 2010 June 16 |
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. Specifically, |
||
12 | # it tests SQLite when using a VFS that claims the SAFE_DELETE property. |
||
13 | # |
||
14 | |||
15 | set testdir [file dirname $argv0] |
||
16 | source $testdir/tester.tcl |
||
17 | source $testdir/lock_common.tcl |
||
18 | source $testdir/malloc_common.tcl |
||
19 | db close |
||
20 | |||
21 | if {[permutation] == "inmemory_journal"} { |
||
22 | finish_test |
||
23 | return |
||
24 | } |
||
25 | |||
26 | set a_string_counter 1 |
||
27 | proc a_string {n} { |
||
28 | global a_string_counter |
||
29 | incr a_string_counter |
||
30 | string range [string repeat "${a_string_counter}." $n] 1 $n |
||
31 | } |
||
32 | |||
33 | # Create a [testvfs] and install it as the default VFS. Set the device |
||
34 | # characteristics flags to "SAFE_DELETE". |
||
35 | # |
||
36 | testvfs tvfs -default 1 |
||
37 | tvfs devchar undeletable_when_open |
||
38 | |||
39 | # Set up a hook so that each time a journal file is opened, closed or |
||
40 | # deleted, the method name ("xOpen", "xClose" or "xDelete") and the final |
||
41 | # segment of the journal file-name (i.e. "test.db-journal") are appended to |
||
42 | # global list variable $::oplog. |
||
43 | # |
||
44 | tvfs filter {xOpen xClose xDelete} |
||
45 | tvfs script journal_op_catcher |
||
46 | proc journal_op_catcher {method filename args} { |
||
47 | |||
48 | # If global variable ::tvfs_error_on_write is defined, then return an |
||
49 | # IO error to every attempt to modify the file-system. Otherwise, return |
||
50 | # SQLITE_OK. |
||
51 | # |
||
52 | if {[info exists ::tvfs_error_on_write]} { |
||
53 | if {[lsearch {xDelete xWrite xTruncate} $method]>=0} { |
||
54 | return SQLITE_IOERR |
||
55 | } |
||
56 | } |
||
57 | |||
58 | # The rest of this command only deals with xOpen(), xClose() and xDelete() |
||
59 | # operations on journal files. If this invocation does not represent such |
||
60 | # an operation, return with no further ado. |
||
61 | # |
||
62 | set f [file tail $filename] |
||
63 | if {[string match *journal $f]==0} return |
||
64 | if {[lsearch {xOpen xDelete xClose} $method]<0} return |
||
65 | |||
66 | # Append a record of this operation to global list variable $::oplog. |
||
67 | # |
||
68 | lappend ::oplog $method $f |
||
69 | |||
70 | # If this is an attempt to delete a journal file for which there exists |
||
71 | # one ore more open handles, return an error. The code in test_vfs.c |
||
72 | # will not invoke the xDelete method of the "real" VFS in this case. |
||
73 | # |
||
74 | if {[info exists ::open_journals($f)]==0} { set ::open_journals($f) 0 } |
||
75 | switch -- $method { |
||
76 | xOpen { incr ::open_journals($f) +1 } |
||
77 | xClose { incr ::open_journals($f) -1 } |
||
78 | xDelete { if {$::open_journals($f)>0} { return SQLITE_IOERR } } |
||
79 | } |
||
80 | |||
81 | return "" |
||
82 | } |
||
83 | |||
84 | |||
85 | do_test journal2-1.1 { |
||
86 | set ::oplog [list] |
||
87 | sqlite3 db test.db |
||
88 | execsql { CREATE TABLE t1(a, b) } |
||
89 | set ::oplog |
||
90 | } {xOpen test.db-journal xClose test.db-journal xDelete test.db-journal} |
||
91 | do_test journal2-1.2 { |
||
92 | set ::oplog [list] |
||
93 | execsql { |
||
94 | PRAGMA journal_mode = truncate; |
||
95 | INSERT INTO t1 VALUES(1, 2); |
||
96 | } |
||
97 | set ::oplog |
||
98 | } {xOpen test.db-journal} |
||
99 | do_test journal2-1.3 { |
||
100 | set ::oplog [list] |
||
101 | execsql { INSERT INTO t1 VALUES(3, 4) } |
||
102 | set ::oplog |
||
103 | } {} |
||
104 | do_test journal2-1.4 { execsql { SELECT * FROM t1 } } {1 2 3 4} |
||
105 | |||
106 | # Add a second connection. This connection attempts to commit data in |
||
107 | # journal_mode=DELETE mode. When it tries to delete the journal file, |
||
108 | # the VFS layer returns an IO error. |
||
109 | # |
||
110 | do_test journal2-1.5 { |
||
111 | set ::oplog [list] |
||
112 | sqlite3 db2 test.db |
||
113 | execsql { PRAGMA journal_mode = delete } db2 |
||
114 | catchsql { INSERT INTO t1 VALUES(5, 6) } db2 |
||
115 | } {1 {disk I/O error}} |
||
116 | do_test journal2-1.6 { file exists test.db-journal } 1 |
||
117 | do_test journal2-1.7 { execsql { SELECT * FROM t1 } } {1 2 3 4} |
||
118 | do_test journal2-1.8 { |
||
119 | execsql { PRAGMA journal_mode = truncate } db2 |
||
120 | execsql { INSERT INTO t1 VALUES(5, 6) } db2 |
||
121 | } {} |
||
122 | do_test journal2-1.9 { execsql { SELECT * FROM t1 } } {1 2 3 4 5 6} |
||
123 | |||
124 | # Grow the database until it is reasonably large. |
||
125 | # |
||
126 | do_test journal2-1.10 { |
||
127 | db2 close |
||
128 | db func a_string a_string |
||
129 | execsql { |
||
130 | CREATE TABLE t2(a UNIQUE, b UNIQUE); |
||
131 | INSERT INTO t2 VALUES(a_string(200), a_string(300)); |
||
132 | INSERT INTO t2 SELECT a_string(200), a_string(300) FROM t2; -- 2 |
||
133 | INSERT INTO t2 SELECT a_string(200), a_string(300) FROM t2; -- 4 |
||
134 | INSERT INTO t2 SELECT a_string(200), a_string(300) FROM t2; -- 8 |
||
135 | INSERT INTO t2 SELECT a_string(200), a_string(300) FROM t2; -- 16 |
||
136 | INSERT INTO t2 SELECT a_string(200), a_string(300) FROM t2; -- 32 |
||
137 | INSERT INTO t2 SELECT a_string(200), a_string(300) FROM t2; -- 64 |
||
138 | } |
||
139 | file size test.db-journal |
||
140 | } {0} |
||
141 | do_test journal2-1.11 { |
||
142 | set sz [expr [file size test.db] / 1024] |
||
143 | expr {$sz>120 && $sz<200} |
||
144 | } 1 |
||
145 | |||
146 | # Using new connection [db2] (with journal_mode=DELETE), write a lot of |
||
147 | # data to the database. So that many pages within the database file are |
||
148 | # modified before the transaction is committed. |
||
149 | # |
||
150 | # Then, enable simulated IO errors in all calls to xDelete, xWrite |
||
151 | # and xTruncate before committing the transaction and closing the |
||
152 | # database file. From the point of view of other file-system users, it |
||
153 | # appears as if the process hosting [db2] unexpectedly exited. |
||
154 | # |
||
155 | do_test journal2-1.12 { |
||
156 | sqlite3 db2 test.db |
||
157 | execsql { |
||
158 | PRAGMA cache_size = 10; |
||
159 | BEGIN; |
||
160 | INSERT INTO t2 SELECT randomblob(200), randomblob(300) FROM t2; -- 128 |
||
161 | } db2 |
||
162 | } {} |
||
163 | do_test journal2-1.13 { |
||
164 | tvfs filter {xOpen xClose xDelete xWrite xTruncate} |
||
165 | set ::tvfs_error_on_write 1 |
||
166 | catchsql { COMMIT } db2 |
||
167 | } {1 {disk I/O error}} |
||
168 | db2 close |
||
169 | unset ::tvfs_error_on_write |
||
170 | file copy -force test.db testX.db |
||
171 | |||
172 | do_test journal2-1.14 { file exists test.db-journal } 1 |
||
173 | do_test journal2-1.15 { |
||
174 | execsql { |
||
175 | SELECT count(*) FROM t2; |
||
176 | PRAGMA integrity_check; |
||
177 | } |
||
178 | } {64 ok} |
||
179 | |||
180 | # This block checks that in the test case above, connection [db2] really |
||
181 | # did begin writing to the database file before it hit IO errors. If |
||
182 | # this is true, then the copy of the database file made before [db] |
||
183 | # rolled back the hot journal should fail the integrity-check. |
||
184 | # |
||
185 | do_test journal2-1.16 { |
||
186 | set sz [expr [file size testX.db] / 1024] |
||
187 | expr {$sz>240 && $sz<400} |
||
188 | } 1 |
||
189 | do_test journal2-1.17 { |
||
190 | expr {[catchsql { PRAGMA integrity_check } db] == "0 ok"} |
||
191 | } {1} |
||
192 | do_test journal2-1.20 { |
||
193 | sqlite3 db2 testX.db |
||
194 | expr {[catchsql { PRAGMA integrity_check } db2] == "0 ok"} |
||
195 | } {0} |
||
196 | do_test journal2-1.21 { |
||
197 | db2 close |
||
198 | } {} |
||
199 | db close |
||
200 | |||
201 | #------------------------------------------------------------------------- |
||
202 | # Test that it is possible to switch from journal_mode=truncate to |
||
203 | # journal_mode=WAL on a SAFE_DELETE file-system. SQLite should close and |
||
204 | # delete the journal file when committing the transaction that switches |
||
205 | # the system to WAL mode. |
||
206 | # |
||
207 | ifcapable wal { |
||
208 | do_test journal2-2.1 { |
||
209 | faultsim_delete_and_reopen |
||
210 | set ::oplog [list] |
||
211 | execsql { PRAGMA journal_mode = persist } |
||
212 | set ::oplog |
||
213 | } {} |
||
214 | do_test journal2-2.2 { |
||
215 | execsql { |
||
216 | CREATE TABLE t1(x); |
||
217 | INSERT INTO t1 VALUES(3.14159); |
||
218 | } |
||
219 | set ::oplog |
||
220 | } {xOpen test.db-journal} |
||
221 | do_test journal2-2.3 { |
||
222 | expr {[file size test.db-journal] > 512} |
||
223 | } {1} |
||
224 | do_test journal2-2.4 { |
||
225 | set ::oplog [list] |
||
226 | execsql { PRAGMA journal_mode = WAL } |
||
227 | set ::oplog |
||
228 | } {xClose test.db-journal xDelete test.db-journal} |
||
229 | db close |
||
230 | } |
||
231 | |||
232 | tvfs delete |
||
233 | finish_test |
||
234 |