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