wasCSharpSQLite – Blame information for rev 7
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | # 2007 May 1 |
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 | # |
||
12 | # $Id: incrblob.test,v 1.24 2009/06/19 22:23:42 drh Exp $ |
||
13 | # |
||
14 | |||
15 | set testdir [file dirname $argv0] |
||
16 | source $testdir/tester.tcl |
||
17 | |||
18 | ifcapable {!autovacuum || !pragma || !incrblob} { |
||
19 | finish_test |
||
20 | return |
||
21 | } |
||
22 | |||
23 | do_test incrblob-1.1 { |
||
24 | execsql { |
||
25 | CREATE TABLE blobs(k PRIMARY KEY, v BLOB); |
||
26 | INSERT INTO blobs VALUES('one', X'0102030405060708090A'); |
||
27 | INSERT INTO blobs VALUES('two', X'0A090807060504030201'); |
||
28 | } |
||
29 | } {} |
||
30 | |||
31 | do_test incrblob-1.2.1 { |
||
32 | set ::blob [db incrblob blobs v 1] |
||
33 | string match incrblob_* $::blob |
||
34 | } {1} |
||
35 | unset -nocomplain data |
||
36 | do_test incrblob-1.2.2 { |
||
37 | binary scan [read $::blob] c* data |
||
38 | set data |
||
39 | } {1 2 3 4 5 6 7 8 9 10} |
||
40 | do_test incrblob-1.2.3 { |
||
41 | seek $::blob 0 |
||
42 | puts -nonewline $::blob "1234567890" |
||
43 | flush $::blob |
||
44 | } {} |
||
45 | do_test incrblob-1.2.4 { |
||
46 | seek $::blob 0 |
||
47 | binary scan [read $::blob] c* data |
||
48 | set data |
||
49 | } {49 50 51 52 53 54 55 56 57 48} |
||
50 | do_test incrblob-1.2.5 { |
||
51 | close $::blob |
||
52 | } {} |
||
53 | do_test incrblob-1.2.6 { |
||
54 | execsql { |
||
55 | SELECT v FROM blobs WHERE rowid = 1; |
||
56 | } |
||
57 | } {1234567890} |
||
58 | |||
59 | #-------------------------------------------------------------------- |
||
60 | # Test cases incrblob-1.3.X check that it is possible to read and write |
||
61 | # regions of a blob that lie on overflow pages. |
||
62 | # |
||
63 | do_test incrblob-1.3.1 { |
||
64 | set ::str "[string repeat . 10000]" |
||
65 | execsql { |
||
66 | INSERT INTO blobs(rowid, k, v) VALUES(3, 'three', $::str); |
||
67 | } |
||
68 | } {} |
||
69 | |||
70 | do_test incrblob-1.3.2 { |
||
71 | set ::blob [db incrblob blobs v 3] |
||
72 | seek $::blob 8500 |
||
73 | read $::blob 10 |
||
74 | } {..........} |
||
75 | do_test incrblob-1.3.3 { |
||
76 | seek $::blob 8500 |
||
77 | puts -nonewline $::blob 1234567890 |
||
78 | } {} |
||
79 | do_test incrblob-1.3.4 { |
||
80 | seek $::blob 8496 |
||
81 | read $::blob 10 |
||
82 | } {....123456} |
||
83 | do_test incrblob-1.3.10 { |
||
84 | close $::blob |
||
85 | } {} |
||
86 | |||
87 | #------------------------------------------------------------------------ |
||
88 | # incrblob-2.*: |
||
89 | # |
||
90 | # Test that the following operations use ptrmap pages to reduce |
||
91 | # unnecessary reads: |
||
92 | # |
||
93 | # * Reading near the end of a blob, |
||
94 | # * Writing near the end of a blob, and |
||
95 | # * SELECT a column value that is located on an overflow page. |
||
96 | # |
||
97 | proc nRead {db} { |
||
98 | set bt [btree_from_db $db] |
||
99 | db_enter $db |
||
100 | array set stats [btree_pager_stats $bt] |
||
101 | db_leave $db |
||
102 | return $stats(read) |
||
103 | } |
||
104 | proc nWrite {db} { |
||
105 | set bt [btree_from_db $db] |
||
106 | db_enter $db |
||
107 | array set stats [btree_pager_stats $bt] |
||
108 | db_leave $db |
||
109 | return $stats(write) |
||
110 | } |
||
111 | |||
112 | sqlite3_soft_heap_limit 0 |
||
113 | |||
114 | foreach AutoVacuumMode [list 0 1] { |
||
115 | |||
116 | if {$AutoVacuumMode>0} { |
||
117 | ifcapable !autovacuum { |
||
118 | break |
||
119 | } |
||
120 | } |
||
121 | |||
122 | db close |
||
123 | file delete -force test.db test.db-journal |
||
124 | |||
125 | sqlite3 db test.db |
||
126 | execsql "PRAGMA auto_vacuum = $AutoVacuumMode" |
||
127 | |||
128 | do_test incrblob-2.$AutoVacuumMode.1 { |
||
129 | set ::str [string repeat abcdefghij 2900] |
||
130 | execsql { |
||
131 | BEGIN; |
||
132 | CREATE TABLE blobs(k PRIMARY KEY, v BLOB, i INTEGER); |
||
133 | DELETE FROM blobs; |
||
134 | INSERT INTO blobs VALUES('one', $::str || randstr(500,500), 45); |
||
135 | COMMIT; |
||
136 | } |
||
137 | expr [file size test.db]/1024 |
||
138 | } [expr 31 + $AutoVacuumMode] |
||
139 | |||
140 | ifcapable autovacuum { |
||
141 | do_test incrblob-2.$AutoVacuumMode.2 { |
||
142 | execsql { |
||
143 | PRAGMA auto_vacuum; |
||
144 | } |
||
145 | } $AutoVacuumMode |
||
146 | } |
||
147 | |||
148 | do_test incrblob-2.$AutoVacuumMode.3 { |
||
149 | # Open and close the db to make sure the page cache is empty. |
||
150 | db close |
||
151 | sqlite3 db test.db |
||
152 | |||
153 | # Read the last 20 bytes of the blob via a blob handle. |
||
154 | set ::blob [db incrblob blobs v 1] |
||
155 | seek $::blob -20 end |
||
156 | set ::fragment [read $::blob] |
||
157 | close $::blob |
||
158 | |||
159 | # If the database is not in auto-vacuum mode, the whole of |
||
160 | # the overflow-chain must be scanned. In auto-vacuum mode, |
||
161 | # sqlite uses the ptrmap pages to avoid reading the other pages. |
||
162 | # |
||
163 | nRead db |
||
164 | } [expr $AutoVacuumMode ? 4 : 30] |
||
165 | |||
166 | do_test incrblob-2.$AutoVacuumMode.4 { |
||
167 | string range [db one {SELECT v FROM blobs}] end-19 end |
||
168 | } $::fragment |
||
169 | |||
170 | do_test incrblob-2.$AutoVacuumMode.5 { |
||
171 | # Open and close the db to make sure the page cache is empty. |
||
172 | db close |
||
173 | sqlite3 db test.db |
||
174 | |||
175 | # Write the second-to-last 20 bytes of the blob via a blob handle. |
||
176 | # |
||
177 | set ::blob [db incrblob blobs v 1] |
||
178 | seek $::blob -40 end |
||
179 | puts -nonewline $::blob "1234567890abcdefghij" |
||
180 | flush $::blob |
||
181 | |||
182 | # If the database is not in auto-vacuum mode, the whole of |
||
183 | # the overflow-chain must be scanned. In auto-vacuum mode, |
||
184 | # sqlite uses the ptrmap pages to avoid reading the other pages. |
||
185 | # |
||
186 | nRead db |
||
187 | } [expr $AutoVacuumMode ? 4 : 30] |
||
188 | |||
189 | # Pages 1 (the write-counter) and 32 (the blob data) were written. |
||
190 | do_test incrblob-2.$AutoVacuumMode.6 { |
||
191 | close $::blob |
||
192 | nWrite db |
||
193 | } 2 |
||
194 | |||
195 | do_test incrblob-2.$AutoVacuumMode.7 { |
||
196 | string range [db one {SELECT v FROM blobs}] end-39 end-20 |
||
197 | } "1234567890abcdefghij" |
||
198 | |||
199 | do_test incrblob-2.$AutoVacuumMode.8 { |
||
200 | # Open and close the db to make sure the page cache is empty. |
||
201 | db close |
||
202 | sqlite3 db test.db |
||
203 | |||
204 | execsql { SELECT i FROM blobs } |
||
205 | } {45} |
||
206 | |||
207 | do_test incrblob-2.$AutoVacuumMode.9 { |
||
208 | nRead db |
||
209 | } [expr $AutoVacuumMode ? 4 : 30] |
||
210 | } |
||
211 | sqlite3_soft_heap_limit $cmdlinearg(soft-heap-limit) |
||
212 | |||
213 | #------------------------------------------------------------------------ |
||
214 | # incrblob-3.*: |
||
215 | # |
||
216 | # Test the outcome of trying to write to a read-only blob handle. |
||
217 | # |
||
218 | do_test incrblob-3.1 { |
||
219 | set ::blob [db incrblob -readonly blobs v 1] |
||
220 | seek $::blob -40 end |
||
221 | read $::blob 20 |
||
222 | } "1234567890abcdefghij" |
||
223 | do_test incrblob-3.2 { |
||
224 | seek $::blob 0 |
||
225 | set rc [catch { |
||
226 | puts -nonewline $::blob "helloworld" |
||
227 | } msg] |
||
228 | close $::blob |
||
229 | list $rc $msg |
||
230 | } "1 {channel \"$::blob\" wasn't opened for writing}" |
||
231 | |||
232 | do_test incrblob-3.3 { |
||
233 | set ::blob [db incrblob -readonly blobs v 1] |
||
234 | seek $::blob -40 end |
||
235 | read $::blob 20 |
||
236 | } "1234567890abcdefghij" |
||
237 | do_test incrblob-3.4 { |
||
238 | set rc [catch { |
||
239 | sqlite3_blob_write $::blob 20 "qwertyuioplkjhgfds" |
||
240 | } msg] |
||
241 | list $rc $msg |
||
242 | } {1 SQLITE_READONLY} |
||
243 | catch {close $::blob} |
||
244 | |||
245 | #------------------------------------------------------------------------ |
||
246 | # incrblob-4.*: |
||
247 | # |
||
248 | # Try a couple of error conditions: |
||
249 | # |
||
250 | # 4.1 - Attempt to open a row that does not exist. |
||
251 | # 4.2 - Attempt to open a column that does not exist. |
||
252 | # 4.3 - Attempt to open a table that does not exist. |
||
253 | # 4.4 - Attempt to open a database that does not exist. |
||
254 | # |
||
255 | # 4.5 - Attempt to open an integer |
||
256 | # 4.6 - Attempt to open a real value |
||
257 | # 4.7 - Attempt to open an SQL null |
||
258 | # |
||
259 | # 4.8 - Attempt to open an indexed column for writing |
||
260 | # 4.9 - Attempt to open an indexed column for reading (this works) |
||
261 | # |
||
262 | # 4.11 - Attempt to open a column of a view. |
||
263 | # 4.12 - Attempt to open a column of a virtual table. |
||
264 | # |
||
265 | do_test incrblob-4.1 { |
||
266 | set rc [catch { |
||
267 | set ::blob [db incrblob blobs v 2] |
||
268 | } msg ] |
||
269 | list $rc $msg |
||
270 | } {1 {no such rowid: 2}} |
||
271 | do_test incrblob-4.2 { |
||
272 | set rc [catch { |
||
273 | set ::blob [db incrblob blobs blue 1] |
||
274 | } msg ] |
||
275 | list $rc $msg |
||
276 | } {1 {no such column: "blue"}} |
||
277 | do_test incrblob-4.3 { |
||
278 | set rc [catch { |
||
279 | set ::blob [db incrblob nosuchtable blue 1] |
||
280 | } msg ] |
||
281 | list $rc $msg |
||
282 | } {1 {no such table: main.nosuchtable}} |
||
283 | do_test incrblob-4.4 { |
||
284 | set rc [catch { |
||
285 | set ::blob [db incrblob nosuchdb blobs v 1] |
||
286 | } msg ] |
||
287 | list $rc $msg |
||
288 | } {1 {no such table: nosuchdb.blobs}} |
||
289 | |||
290 | do_test incrblob-4.5 { |
||
291 | set rc [catch { |
||
292 | set ::blob [db incrblob blobs i 1] |
||
293 | } msg ] |
||
294 | list $rc $msg |
||
295 | } {1 {cannot open value of type integer}} |
||
296 | do_test incrblob-4.6 { |
||
297 | execsql { |
||
298 | INSERT INTO blobs(k, v, i) VALUES(123, 567.765, NULL); |
||
299 | } |
||
300 | set rc [catch { |
||
301 | set ::blob [db incrblob blobs v 2] |
||
302 | } msg ] |
||
303 | list $rc $msg |
||
304 | } {1 {cannot open value of type real}} |
||
305 | do_test incrblob-4.7 { |
||
306 | set rc [catch { |
||
307 | set ::blob [db incrblob blobs i 2] |
||
308 | } msg ] |
||
309 | list $rc $msg |
||
310 | } {1 {cannot open value of type null}} |
||
311 | |||
312 | do_test incrblob-4.8.1 { |
||
313 | execsql { |
||
314 | INSERT INTO blobs(k, v, i) VALUES(X'010203040506070809', 'hello', 'world'); |
||
315 | } |
||
316 | set rc [catch { |
||
317 | set ::blob [db incrblob blobs k 3] |
||
318 | } msg ] |
||
319 | list $rc $msg |
||
320 | } {1 {cannot open indexed column for writing}} |
||
321 | do_test incrblob-4.8.2 { |
||
322 | execsql { |
||
323 | CREATE TABLE t3(a INTEGER PRIMARY KEY, b); |
||
324 | INSERT INTO t3 VALUES(1, 2); |
||
325 | } |
||
326 | set rc [catch { |
||
327 | set ::blob [db incrblob -readonly t3 a 1] |
||
328 | } msg ] |
||
329 | list $rc $msg |
||
330 | } {1 {cannot open value of type null}} |
||
331 | do_test incrblob-4.8.3 { |
||
332 | set rc [catch { |
||
333 | set ::blob [db incrblob -readonly t3 rowid 1] |
||
334 | } msg ] |
||
335 | list $rc $msg |
||
336 | } {1 {no such column: "rowid"}} |
||
337 | |||
338 | do_test incrblob-4.9.1 { |
||
339 | set rc [catch { |
||
340 | set ::blob [db incrblob -readonly blobs k 3] |
||
341 | } msg] |
||
342 | } {0} |
||
343 | do_test incrblob-4.9.2 { |
||
344 | binary scan [read $::blob] c* c |
||
345 | close $::blob |
||
346 | set c |
||
347 | } {1 2 3 4 5 6 7 8 9} |
||
348 | |||
349 | do_test incrblob-4.10 { |
||
350 | set ::blob [db incrblob -readonly blobs k 3] |
||
351 | set rc [catch { sqlite3_blob_read $::blob 10 100 } msg] |
||
352 | list $rc $msg |
||
353 | } {1 SQLITE_ERROR} |
||
354 | do_test incrblob-4.10.2 { |
||
355 | close $::blob |
||
356 | } {} |
||
357 | |||
358 | ifcapable view { |
||
359 | do_test incrblob-4.11 { |
||
360 | execsql { CREATE VIEW blobs_view AS SELECT k, v, i FROM blobs } |
||
361 | set rc [catch { db incrblob blobs_view v 3 } msg] |
||
362 | list $rc $msg |
||
363 | } {1 {cannot open view: blobs_view}} |
||
364 | } |
||
365 | ifcapable vtab { |
||
366 | register_echo_module [sqlite3_connection_pointer db] |
||
367 | do_test incrblob-4.12 { |
||
368 | execsql { CREATE VIRTUAL TABLE blobs_echo USING echo(blobs) } |
||
369 | set rc [catch { db incrblob blobs_echo v 3 } msg] |
||
370 | list $rc $msg |
||
371 | } {1 {cannot open virtual table: blobs_echo}} |
||
372 | } |
||
373 | |||
374 | |||
375 | #------------------------------------------------------------------------ |
||
376 | # incrblob-5.*: |
||
377 | # |
||
378 | # Test that opening a blob in an attached database works. |
||
379 | # |
||
380 | ifcapable attach { |
||
381 | do_test incrblob-5.1 { |
||
382 | file delete -force test2.db test2.db-journal |
||
383 | set ::size [expr [file size [info script]]] |
||
384 | execsql { |
||
385 | ATTACH 'test2.db' AS aux; |
||
386 | CREATE TABLE aux.files(name, text); |
||
387 | INSERT INTO aux.files VALUES('this one', zeroblob($::size)); |
||
388 | } |
||
389 | set fd [db incrblob aux files text 1] |
||
390 | fconfigure $fd -translation binary |
||
391 | set fd2 [open [info script]] |
||
392 | fconfigure $fd2 -translation binary |
||
393 | puts -nonewline $fd [read $fd2] |
||
394 | close $fd |
||
395 | close $fd2 |
||
396 | set ::text [db one {select text from aux.files}] |
||
397 | string length $::text |
||
398 | } [file size [info script]] |
||
399 | do_test incrblob-5.2 { |
||
400 | set fd2 [open [info script]] |
||
401 | fconfigure $fd2 -translation binary |
||
402 | set ::data [read $fd2] |
||
403 | close $fd2 |
||
404 | set ::data |
||
405 | } $::text |
||
406 | } |
||
407 | |||
408 | # free memory |
||
409 | unset -nocomplain ::data |
||
410 | unset -nocomplain ::text |
||
411 | |||
412 | #------------------------------------------------------------------------ |
||
413 | # incrblob-6.*: |
||
414 | # |
||
415 | # Test that opening a blob for write-access is impossible if |
||
416 | # another connection has the database RESERVED lock. |
||
417 | # |
||
418 | # Then test that blob writes that take place inside of a |
||
419 | # transaction are not visible to external connections until |
||
420 | # after the transaction is commited and the blob channel |
||
421 | # closed. |
||
422 | # |
||
423 | # This test does not work with the "memsubsys1" configuration. |
||
424 | # Permutation memsubsys1 configures a very small static allocation |
||
425 | # for use as page-cache memory. This causes SQLite to upgrade |
||
426 | # to an exclusive lock when writing earlier than usual, which |
||
427 | # makes some of these tests fail. |
||
428 | # |
||
429 | sqlite3_soft_heap_limit 0 |
||
430 | if {[permutation] != "memsubsys1"} { |
||
431 | do_test incrblob-6.1 { |
||
432 | sqlite3 db2 test.db |
||
433 | execsql { |
||
434 | BEGIN; |
||
435 | INSERT INTO blobs(k, v, i) VALUES('a', 'different', 'connection'); |
||
436 | } db2 |
||
437 | } {} |
||
438 | do_test incrblob-6.2 { |
||
439 | execsql { |
||
440 | SELECT rowid FROM blobs |
||
441 | } |
||
442 | } {1 2 3} |
||
443 | do_test incrblob-6.3 { |
||
444 | set rc [catch { |
||
445 | db incrblob blobs v 1 |
||
446 | } msg] |
||
447 | list $rc $msg |
||
448 | } {1 {database is locked}} |
||
449 | do_test incrblob-6.4 { |
||
450 | set rc [catch { |
||
451 | db incrblob blobs v 3 |
||
452 | } msg] |
||
453 | list $rc $msg |
||
454 | } {1 {database is locked}} |
||
455 | do_test incrblob-6.5 { |
||
456 | set ::blob [db incrblob -readonly blobs v 3] |
||
457 | read $::blob |
||
458 | } {hello} |
||
459 | do_test incrblob-6.6 { |
||
460 | close $::blob |
||
461 | } {} |
||
462 | |||
463 | do_test incrblob-6.7 { |
||
464 | set ::blob [db2 incrblob blobs i 4] |
||
465 | gets $::blob |
||
466 | } {connection} |
||
467 | do_test incrblob-6.8 { |
||
468 | tell $::blob |
||
469 | } {10} |
||
470 | do_test incrblob-6.9 { |
||
471 | seek $::blob 0 |
||
472 | puts -nonewline $::blob "invocation" |
||
473 | flush $::blob |
||
474 | } {} |
||
475 | |||
476 | # At this point rollback should be illegal (because |
||
477 | # there is an open blob channel). But commit is also illegal because |
||
478 | # the open blob is read-write. |
||
479 | # |
||
480 | do_test incrblob-6.10 { |
||
481 | catchsql { |
||
482 | ROLLBACK; |
||
483 | } db2 |
||
484 | } {1 {cannot rollback transaction - SQL statements in progress}} |
||
485 | do_test incrblob-6.11 { |
||
486 | catchsql { |
||
487 | COMMIT; |
||
488 | } db2 |
||
489 | } {1 {cannot commit transaction - SQL statements in progress}} |
||
490 | |||
491 | do_test incrblob-6.12 { |
||
492 | execsql { |
||
493 | SELECT * FROM blobs WHERE rowid = 4; |
||
494 | } |
||
495 | } {} |
||
496 | do_test incrblob-6.13 { |
||
497 | close $::blob |
||
498 | } {} |
||
499 | do_test incrblob-6.14 { |
||
500 | catchsql { |
||
501 | COMMIT; |
||
502 | } db2 |
||
503 | } {0 {}} |
||
504 | do_test incrblob-6.15 { |
||
505 | execsql { |
||
506 | SELECT * FROM blobs WHERE rowid = 4; |
||
507 | } |
||
508 | } {a different invocation} |
||
509 | db2 close |
||
510 | } |
||
511 | sqlite3_soft_heap_limit $cmdlinearg(soft-heap-limit) |
||
512 | |||
513 | #----------------------------------------------------------------------- |
||
514 | # The following tests verify the behaviour of the incremental IO |
||
515 | # APIs in the following cases: |
||
516 | # |
||
517 | # 7.1 A row that containing an open blob is modified. |
||
518 | # |
||
519 | # 7.2 A CREATE TABLE requires that an overflow page that is part |
||
520 | # of an open blob is moved. |
||
521 | # |
||
522 | # 7.3 An INCREMENTAL VACUUM moves an overflow page that is part |
||
523 | # of an open blob. |
||
524 | # |
||
525 | # In the first case above, correct behaviour is for all subsequent |
||
526 | # read/write operations on the blob-handle to return SQLITE_ABORT. |
||
527 | # More accurately, blob-handles are invalidated whenever the table |
||
528 | # they belong to is written to. |
||
529 | # |
||
530 | # The second two cases have no external effect. They are testing |
||
531 | # that the internal cache of overflow page numbers is correctly |
||
532 | # invalidated. |
||
533 | # |
||
534 | do_test incrblob-7.1.0 { |
||
535 | execsql { |
||
536 | BEGIN; |
||
537 | DROP TABLE blobs; |
||
538 | CREATE TABLE t1 (a, b, c, d BLOB); |
||
539 | INSERT INTO t1(a, b, c, d) VALUES(1, 2, 3, 4); |
||
540 | COMMIT; |
||
541 | } |
||
542 | } {} |
||
543 | |||
544 | foreach {tn arg} {1 "" 2 -readonly} { |
||
545 | |||
546 | execsql { |
||
547 | UPDATE t1 SET d = zeroblob(10000); |
||
548 | } |
||
549 | |||
550 | do_test incrblob-7.1.$tn.1 { |
||
551 | set ::b [eval db incrblob $arg t1 d 1] |
||
552 | binary scan [sqlite3_blob_read $::b 5000 5] c* c |
||
553 | set c |
||
554 | } {0 0 0 0 0} |
||
555 | do_test incrblob-7.1.$tn.2 { |
||
556 | execsql { |
||
557 | UPDATE t1 SET d = 15; |
||
558 | } |
||
559 | } {} |
||
560 | do_test incrblob-7.1.$tn.3 { |
||
561 | set rc [catch { sqlite3_blob_read $::b 5000 5 } msg] |
||
562 | list $rc $msg |
||
563 | } {1 SQLITE_ABORT} |
||
564 | do_test incrblob-7.1.$tn.4 { |
||
565 | execsql { |
||
566 | SELECT d FROM t1; |
||
567 | } |
||
568 | } {15} |
||
569 | do_test incrblob-7.1.$tn.5 { |
||
570 | set rc [catch { close $::b } msg] |
||
571 | list $rc $msg |
||
572 | } {0 {}} |
||
573 | do_test incrblob-7.1.$tn.6 { |
||
574 | execsql { |
||
575 | SELECT d FROM t1; |
||
576 | } |
||
577 | } {15} |
||
578 | |||
579 | } |
||
580 | |||
581 | set fd [open [info script]] |
||
582 | fconfigure $fd -translation binary |
||
583 | set ::data [read $fd 14000] |
||
584 | close $fd |
||
585 | |||
586 | db close |
||
587 | file delete -force test.db test.db-journal |
||
588 | sqlite3 db test.db |
||
589 | |||
590 | do_test incrblob-7.2.1 { |
||
591 | execsql { |
||
592 | PRAGMA auto_vacuum = "incremental"; |
||
593 | CREATE TABLE t1(a INTEGER PRIMARY KEY, b); -- root@page3 |
||
594 | INSERT INTO t1 VALUES(123, $::data); |
||
595 | } |
||
596 | set ::b [db incrblob -readonly t1 b 123] |
||
597 | fconfigure $::b -translation binary |
||
598 | read $::b |
||
599 | } $::data |
||
600 | do_test incrblob-7.2.2 { |
||
601 | execsql { |
||
602 | CREATE TABLE t2(a INTEGER PRIMARY KEY, b); -- root@page4 |
||
603 | } |
||
604 | seek $::b 0 |
||
605 | read $::b |
||
606 | } $::data |
||
607 | do_test incrblob-7.2.3 { |
||
608 | close $::b |
||
609 | execsql { |
||
610 | SELECT rootpage FROM sqlite_master; |
||
611 | } |
||
612 | } {3 4} |
||
613 | |||
614 | set ::otherdata "[string range $::data 0 1000][string range $::data 1001 end]" |
||
615 | do_test incrblob-7.3.1 { |
||
616 | execsql { |
||
617 | INSERT INTO t2 VALUES(456, $::otherdata); |
||
618 | } |
||
619 | set ::b [db incrblob -readonly t2 b 456] |
||
620 | fconfigure $::b -translation binary |
||
621 | read $::b |
||
622 | } $::otherdata |
||
623 | do_test incrblob-7.3.2 { |
||
624 | expr [file size test.db]/1024 |
||
625 | } 30 |
||
626 | do_test incrblob-7.3.3 { |
||
627 | execsql { |
||
628 | DELETE FROM t1 WHERE a = 123; |
||
629 | PRAGMA INCREMENTAL_VACUUM(0); |
||
630 | } |
||
631 | seek $::b 0 |
||
632 | read $::b |
||
633 | } $::otherdata |
||
634 | |||
635 | # Attempt to write on a read-only blob. Make sure the error code |
||
636 | # gets set. Ticket #2464. |
||
637 | # |
||
638 | do_test incrblob-7.4 { |
||
639 | set rc [catch {sqlite3_blob_write $::b 10 HELLO} msg] |
||
640 | lappend rc $msg |
||
641 | } {1 SQLITE_READONLY} |
||
642 | do_test incrblob-7.5 { |
||
643 | sqlite3_errcode db |
||
644 | } {SQLITE_READONLY} |
||
645 | do_test incrblob-7.6 { |
||
646 | sqlite3_errmsg db |
||
647 | } {attempt to write a readonly database} |
||
648 | |||
649 | # Test that if either the "offset" or "amount" arguments to |
||
650 | # sqlite3_blob_write() are less than zero, SQLITE_ERROR is returned. |
||
651 | # |
||
652 | do_test incrblob-8.1 { |
||
653 | execsql { INSERT INTO t1 VALUES(314159, 'sqlite') } |
||
654 | set ::b [db incrblob t1 b 314159] |
||
655 | fconfigure $::b -translation binary |
||
656 | set rc [catch {sqlite3_blob_write $::b 10 HELLO -1} msg] |
||
657 | lappend rc $msg |
||
658 | } {1 SQLITE_ERROR} |
||
659 | do_test incrblob-8.2 { |
||
660 | sqlite3_errcode db |
||
661 | } {SQLITE_ERROR} |
||
662 | do_test incrblob-8.3 { |
||
663 | set rc [catch {sqlite3_blob_write $::b -1 HELLO 5} msg] |
||
664 | lappend rc $msg |
||
665 | } {1 SQLITE_ERROR} |
||
666 | do_test incrblob-8.4 { |
||
667 | sqlite3_errcode db |
||
668 | } {SQLITE_ERROR} |
||
669 | do_test incrblob-8.5 { |
||
670 | execsql {SELECT b FROM t1 WHERE a = 314159} |
||
671 | } {sqlite} |
||
672 | do_test incrblob-8.6 { |
||
673 | set rc [catch {sqlite3_blob_write $::b 0 etilqs 6} msg] |
||
674 | lappend rc $msg |
||
675 | } {0 {}} |
||
676 | do_test incrblob-8.7 { |
||
677 | execsql {SELECT b FROM t1 WHERE a = 314159} |
||
678 | } {etilqs} |
||
679 | |||
680 | # The following test case exposes an instance in the blob code where |
||
681 | # an error message was set using a call similar to sqlite3_mprintf(zErr), |
||
682 | # where zErr is an arbitrary string. This is no good if the string contains |
||
683 | # characters that can be mistaken for printf() formatting directives. |
||
684 | # |
||
685 | do_test incrblob-9.1 { |
||
686 | list [catch { db incrblob t1 "A tricky column name %s%s" 1 } msg] $msg |
||
687 | } {1 {no such column: "A tricky column name %s%s"}} |
||
688 | |||
689 | |||
690 | finish_test |