aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/libraries/sqlite/unix/sqlite-3.5.1/test/incrblob.test
diff options
context:
space:
mode:
Diffstat (limited to 'libraries/sqlite/unix/sqlite-3.5.1/test/incrblob.test')
-rw-r--r--libraries/sqlite/unix/sqlite-3.5.1/test/incrblob.test597
1 files changed, 597 insertions, 0 deletions
diff --git a/libraries/sqlite/unix/sqlite-3.5.1/test/incrblob.test b/libraries/sqlite/unix/sqlite-3.5.1/test/incrblob.test
new file mode 100644
index 0000000..28d7132
--- /dev/null
+++ b/libraries/sqlite/unix/sqlite-3.5.1/test/incrblob.test
@@ -0,0 +1,597 @@
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.16 2007/09/03 16:45:36 drh Exp $
13#
14
15set testdir [file dirname $argv0]
16source $testdir/tester.tcl
17
18ifcapable {!autovacuum || !pragma || !incrblob} {
19 finish_test
20 return
21}
22
23do_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
31do_test incrblob-1.2.1 {
32 set ::blob [db incrblob blobs v 1]
33 string match incrblob_* $::blob
34} {1}
35do_test incrblob-1.2.2 {
36 binary scan [read $::blob] c* data
37 set data
38} {1 2 3 4 5 6 7 8 9 10}
39do_test incrblob-1.2.3 {
40 seek $::blob 0
41 puts -nonewline $::blob "1234567890"
42 flush $::blob
43} {}
44do_test incrblob-1.2.4 {
45 seek $::blob 0
46 binary scan [read $::blob] c* data
47 set data
48} {49 50 51 52 53 54 55 56 57 48}
49do_test incrblob-1.2.5 {
50 close $::blob
51} {}
52do_test incrblob-1.2.6 {
53 execsql {
54 SELECT v FROM blobs WHERE rowid = 1;
55 }
56} {1234567890}
57
58#--------------------------------------------------------------------
59# Test cases incrblob-1.3.X check that it is possible to read and write
60# regions of a blob that lie on overflow pages.
61#
62do_test incrblob-1.3.1 {
63 set ::str "[string repeat . 10000]"
64 execsql {
65 INSERT INTO blobs(rowid, k, v) VALUES(3, 'three', $::str);
66 }
67} {}
68
69do_test incrblob-1.3.2 {
70 set ::blob [db incrblob blobs v 3]
71 seek $::blob 8500
72 read $::blob 10
73} {..........}
74do_test incrblob-1.3.3 {
75 seek $::blob 8500
76 puts -nonewline $::blob 1234567890
77} {}
78do_test incrblob-1.3.4 {
79 seek $::blob 8496
80 read $::blob 10
81} {....123456}
82do_test incrblob-1.3.10 {
83 close $::blob
84} {}
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#
97proc 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}
104proc 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
112sqlite3_soft_heap_limit 0
113
114foreach 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}
211sqlite3_soft_heap_limit $soft_limit
212
213#------------------------------------------------------------------------
214# incrblob-3.*:
215#
216# Test the outcome of trying to write to a read-only blob handle.
217#
218do_test incrblob-3.1 {
219 set ::blob [db incrblob -readonly blobs v 1]
220 seek $::blob -40 end
221 read $::blob 20
222} "1234567890abcdefghij"
223do_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
232do_test incrblob-3.3 {
233 set ::blob [db incrblob -readonly blobs v 1]
234 seek $::blob -40 end
235 read $::blob 20
236} "1234567890abcdefghij"
237do_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}
243catch {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#
262do_test incrblob-4.1 {
263 set rc [catch {
264 set ::blob [db incrblob blobs v 2]
265 } msg ]
266 list $rc $msg
267} {1 {no such rowid: 2}}
268do_test incrblob-4.2 {
269 set rc [catch {
270 set ::blob [db incrblob blobs blue 1]
271 } msg ]
272 list $rc $msg
273} {1 {no such column: "blue"}}
274do_test incrblob-4.3 {
275 set rc [catch {
276 set ::blob [db incrblob nosuchtable blue 1]
277 } msg ]
278 list $rc $msg
279} {1 {no such table: main.nosuchtable}}
280do_test incrblob-4.4 {
281 set rc [catch {
282 set ::blob [db incrblob nosuchdb blobs v 1]
283 } msg ]
284 list $rc $msg
285} {1 {no such table: nosuchdb.blobs}}
286
287do_test incrblob-4.5 {
288 set rc [catch {
289 set ::blob [db incrblob blobs i 1]
290 } msg ]
291 list $rc $msg
292} {1 {cannot open value of type integer}}
293do_test incrblob-4.6 {
294 execsql {
295 INSERT INTO blobs(k, v, i) VALUES(123, 567.765, NULL);
296 }
297 set rc [catch {
298 set ::blob [db incrblob blobs v 2]
299 } msg ]
300 list $rc $msg
301} {1 {cannot open value of type real}}
302do_test incrblob-4.7 {
303 set rc [catch {
304 set ::blob [db incrblob blobs i 2]
305 } msg ]
306 list $rc $msg
307} {1 {cannot open value of type null}}
308
309do_test incrblob-4.8 {
310 execsql {
311 INSERT INTO blobs(k, v, i) VALUES(X'010203040506070809', 'hello', 'world');
312 }
313 set rc [catch {
314 set ::blob [db incrblob blobs k 3]
315 } msg ]
316 list $rc $msg
317} {1 {cannot open indexed column for writing}}
318
319do_test incrblob-4.9.1 {
320 set rc [catch {
321 set ::blob [db incrblob -readonly blobs k 3]
322 } msg]
323} {0}
324do_test incrblob-4.9.2 {
325 binary scan [read $::blob] c* c
326 close $::blob
327 set c
328} {1 2 3 4 5 6 7 8 9}
329
330do_test incrblob-4.10 {
331 set ::blob [db incrblob -readonly blobs k 3]
332 set rc [catch { sqlite3_blob_read $::blob 10 100 } msg]
333 list $rc $msg
334} {1 SQLITE_ERROR}
335do_test incrblob-4.11 {
336 close $::blob
337} {}
338
339#------------------------------------------------------------------------
340# incrblob-5.*:
341#
342# Test that opening a blob in an attached database works.
343#
344do_test incrblob-5.1 {
345 file delete -force test2.db test2.db-journal
346 set ::size [expr [file size [info script]]]
347 execsql {
348 ATTACH 'test2.db' AS aux;
349 CREATE TABLE aux.files(name, text);
350 INSERT INTO aux.files VALUES('this one', zeroblob($::size));
351 }
352 set fd [db incrblob aux files text 1]
353 fconfigure $fd -translation binary
354 set fd2 [open [info script]]
355 fconfigure $fd2 -translation binary
356 puts -nonewline $fd [read $fd2]
357 close $fd
358 close $fd2
359 set ::text [db one {select text from aux.files}]
360 string length $::text
361} [file size [info script]]
362do_test incrblob-5.2 {
363 set fd2 [open [info script]]
364 fconfigure $fd2 -translation binary
365 set ::data [read $fd2]
366 close $fd2
367 set ::data
368} $::text
369
370# free memory
371unset ::data
372unset ::text
373
374#------------------------------------------------------------------------
375# incrblob-6.*:
376#
377# Test that opening a blob for write-access is impossible if
378# another connection has the database RESERVED lock.
379#
380# Then test that blob writes that take place inside of a
381# transaction are not visible to external connections until
382# after the transaction is commited and the blob channel
383# closed.
384#
385sqlite3_soft_heap_limit 0
386do_test incrblob-6.1 {
387 sqlite3 db2 test.db
388 execsql {
389 BEGIN;
390 INSERT INTO blobs(k, v, i) VALUES('a', 'different', 'connection');
391 } db2
392} {}
393do_test incrblob-6.2 {
394 execsql {
395 SELECT rowid FROM blobs
396 }
397} {1 2 3}
398do_test incrblob-6.3 {
399 set rc [catch {
400 db incrblob blobs v 1
401 } msg]
402 list $rc $msg
403} {1 {database is locked}}
404do_test incrblob-6.4 {
405 set rc [catch {
406 db incrblob blobs v 3
407 } msg]
408 list $rc $msg
409} {1 {database is locked}}
410do_test incrblob-6.5 {
411 set ::blob [db incrblob -readonly blobs v 3]
412 read $::blob
413} {hello}
414do_test incrblob-6.6 {
415 close $::blob
416} {}
417
418do_test incrblob-6.7 {
419 set ::blob [db2 incrblob blobs i 4]
420 gets $::blob
421} {connection}
422do_test incrblob-6.8 {
423 tell $::blob
424} {10}
425do_test incrblob-6.9 {
426 seek $::blob 0
427 puts -nonewline $::blob "invocation"
428 flush $::blob
429} {}
430
431# At this point rollback or commit should be illegal (because
432# there is an open blob channel).
433do_test incrblob-6.10 {
434 catchsql {
435 ROLLBACK;
436 } db2
437} {1 {cannot rollback transaction - SQL statements in progress}}
438do_test incrblob-6.11 {
439 catchsql {
440 COMMIT;
441 } db2
442} {1 {cannot commit transaction - SQL statements in progress}}
443
444do_test incrblob-6.12 {
445 execsql {
446 SELECT * FROM blobs WHERE rowid = 4;
447 }
448} {}
449do_test incrblob-6.13 {
450 close $::blob
451 execsql {
452 COMMIT;
453 } db2
454} {}
455do_test incrblob-6.14 {
456 execsql {
457 SELECT * FROM blobs WHERE rowid = 4;
458 }
459} {a different invocation}
460db2 close
461sqlite3_soft_heap_limit $soft_limit
462
463#-----------------------------------------------------------------------
464# The following tests verify the behaviour of the incremental IO
465# APIs in the following cases:
466#
467# 7.1 A row that containing an open blob is modified.
468#
469# 7.2 A CREATE TABLE requires that an overflow page that is part
470# of an open blob is moved.
471#
472# 7.3 An INCREMENTAL VACUUM moves an overflow page that is part
473# of an open blob.
474#
475# In the first case above, correct behaviour is for all subsequent
476# read/write operations on the blob-handle to return SQLITE_ABORT.
477# More accurately, blob-handles are invalidated whenever the table
478# they belong to is written to.
479#
480# The second two cases have no external effect. They are testing
481# that the internal cache of overflow page numbers is correctly
482# invalidated.
483#
484do_test incrblob-7.1.0 {
485 execsql {
486 BEGIN;
487 DROP TABLE blobs;
488 CREATE TABLE t1 (a, b, c, d BLOB);
489 INSERT INTO t1(a, b, c, d) VALUES(1, 2, 3, 4);
490 COMMIT;
491 }
492} {}
493
494foreach {tn arg} {1 "" 2 -readonly} {
495
496 execsql {
497 UPDATE t1 SET d = zeroblob(10000);
498 }
499
500 do_test incrblob-7.1.$tn.1 {
501 set ::b [eval db incrblob $arg t1 d 1]
502 binary scan [sqlite3_blob_read $::b 5000 5] c* c
503 set c
504 } {0 0 0 0 0}
505 do_test incrblob-7.1.$tn.2 {
506 execsql {
507 UPDATE t1 SET d = 15;
508 }
509 } {}
510 do_test incrblob-7.1.$tn.3 {
511 set rc [catch { sqlite3_blob_read $::b 5000 5 } msg]
512 list $rc $msg
513 } {1 SQLITE_ABORT}
514 do_test incrblob-7.1.$tn.4 {
515 execsql {
516 SELECT d FROM t1;
517 }
518 } {15}
519 do_test incrblob-7.1.$tn.5 {
520 set rc [catch { close $::b } msg]
521 list $rc $msg
522 } {0 {}}
523 do_test incrblob-7.1.$tn.6 {
524 execsql {
525 SELECT d FROM t1;
526 }
527 } {15}
528
529}
530
531set fd [open [info script]]
532fconfigure $fd -translation binary
533set ::data [read $fd 14000]
534close $fd
535
536db close
537file delete -force test.db test.db-journal
538sqlite3 db test.db
539
540do_test incrblob-7.2.1 {
541 execsql {
542 PRAGMA auto_vacuum = "incremental";
543 CREATE TABLE t1(a INTEGER PRIMARY KEY, b); -- root@page3
544 INSERT INTO t1 VALUES(123, $::data);
545 }
546 set ::b [db incrblob -readonly t1 b 123]
547 read $::b
548} $::data
549do_test incrblob-7.2.2 {
550 execsql {
551 CREATE TABLE t2(a INTEGER PRIMARY KEY, b); -- root@page4
552 }
553 seek $::b 0
554 read $::b
555} $::data
556do_test incrblob-7.2.3 {
557 close $::b
558 execsql {
559 SELECT rootpage FROM sqlite_master;
560 }
561} {3 4}
562
563set ::otherdata "[string range $::data 0 1000][string range $::data 1001 end]"
564do_test incrblob-7.3.1 {
565 execsql {
566 INSERT INTO t2 VALUES(456, $::otherdata);
567 }
568 set ::b [db incrblob -readonly t2 b 456]
569 read $::b
570} $::otherdata
571do_test incrblob-7.3.2 {
572 expr [file size test.db]/1024
573} 30
574do_test incrblob-7.3.3 {
575 execsql {
576 DELETE FROM t1 WHERE a = 123;
577 PRAGMA INCREMENTAL_VACUUM(0);
578 }
579 seek $::b 0
580 read $::b
581} $::otherdata
582
583# Attempt to write on a read-only blob. Make sure the error code
584# gets set. Ticket #2464.
585#
586do_test incrblob-7.4 {
587 set rc [catch {sqlite3_blob_write $::b 10 HELLO} msg]
588 lappend rc $msg
589} {1 SQLITE_READONLY}
590do_test incrblob-7.5 {
591 sqlite3_errcode db
592} {SQLITE_READONLY}
593do_test incrblob-7.6 {
594 sqlite3_errmsg db
595} {attempt to write a readonly database}
596
597finish_test