blob: 562c93da3627e74c3f639c177440f3b621e1bc28 [file] [log] [blame]
danielk1977b4e9af92007-05-01 17:49:49 +00001# 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#
drh847d3ab2007-05-04 14:36:22 +000012# $Id: incrblob.test,v 1.7 2007/05/04 14:36:22 drh Exp $
danielk197720713f32007-05-03 11:43:33 +000013#
danielk1977b4e9af92007-05-01 17:49:49 +000014
15set testdir [file dirname $argv0]
16source $testdir/tester.tcl
17
18do_test incrblob-1.1 {
19 execsql {
20 CREATE TABLE blobs(k PRIMARY KEY, v BLOB);
21 INSERT INTO blobs VALUES('one', X'0102030405060708090A');
22 INSERT INTO blobs VALUES('two', X'0A090807060504030201');
23 }
24} {}
25
26do_test incrblob-1.2.1 {
27 set ::blob [db incrblob blobs v 1]
28} {incrblob_1}
29do_test incrblob-1.2.2 {
30 binary scan [read $::blob] c* data
31 set data
32} {1 2 3 4 5 6 7 8 9 10}
33do_test incrblob-1.2.3 {
34 seek $::blob 0
35 puts -nonewline $::blob "1234567890"
36 flush $::blob
37} {}
38do_test incrblob-1.2.4 {
39 seek $::blob 0
40 binary scan [read $::blob] c* data
41 set data
42} {49 50 51 52 53 54 55 56 57 48}
43do_test incrblob-1.2.5 {
44 close $::blob
45} {}
46do_test incrblob-1.2.6 {
47 execsql {
48 SELECT v FROM blobs WHERE rowid = 1;
49 }
50} {1234567890}
51
danielk1977d04417962007-05-02 13:16:30 +000052#--------------------------------------------------------------------
danielk197720713f32007-05-03 11:43:33 +000053# Test cases incrblob-1.3.X check that it is possible to read and write
danielk1977d04417962007-05-02 13:16:30 +000054# regions of a blob that lie on overflow pages.
danielk197720713f32007-05-03 11:43:33 +000055#
56do_test incrblob-1.3.1 {
danielk1977d04417962007-05-02 13:16:30 +000057 set ::str "[string repeat . 10000]"
58 execsql {
59 INSERT INTO blobs(rowid, k, v) VALUES(3, 'three', $::str);
60 }
61} {}
danielk1977b4e9af92007-05-01 17:49:49 +000062
danielk197720713f32007-05-03 11:43:33 +000063do_test incrblob-1.3.2 {
danielk1977d04417962007-05-02 13:16:30 +000064 set ::blob [db incrblob blobs v 3]
65 seek $::blob 8500
66 read $::blob 10
67} {..........}
danielk197720713f32007-05-03 11:43:33 +000068do_test incrblob-1.3.3 {
danielk1977d04417962007-05-02 13:16:30 +000069 seek $::blob 8500
70 puts -nonewline $::blob 1234567890
71} {}
danielk197720713f32007-05-03 11:43:33 +000072do_test incrblob-1.3.4 {
danielk1977d04417962007-05-02 13:16:30 +000073 seek $::blob 8496
74 read $::blob 10
75} {....123456}
danielk197720713f32007-05-03 11:43:33 +000076do_test incrblob-1.3.10 {
danielk1977d04417962007-05-02 13:16:30 +000077 close $::blob
78} {}
79
danielk1977b4e9af92007-05-01 17:49:49 +000080
danielk197720713f32007-05-03 11:43:33 +000081#------------------------------------------------------------------------
danielk19778cbadb02007-05-03 16:31:26 +000082# incrblob-2.*:
83#
84# Test that the following operations use ptrmap pages to reduce
85# unnecessary reads:
danielk197744e6c8d2007-05-03 13:11:32 +000086#
87# * Reading near the end of a blob,
danielk19778cbadb02007-05-03 16:31:26 +000088# * Writing near the end of a blob, and
89# * SELECT a column value that is located on an overflow page.
danielk197720713f32007-05-03 11:43:33 +000090#
91proc nRead {db} {
92 set bt [btree_from_db $db]
93 array set stats [btree_pager_stats $bt]
94 return $stats(read)
95}
danielk19778cbadb02007-05-03 16:31:26 +000096proc nWrite {db} {
97 set bt [btree_from_db $db]
98 array set stats [btree_pager_stats $bt]
99 return $stats(write)
100}
danielk197720713f32007-05-03 11:43:33 +0000101
102foreach AutoVacuumMode [list 0 1] {
103
drh847d3ab2007-05-04 14:36:22 +0000104 if {$AutoVacuumMode>0} {
105 ifcapable !autovacuum {
106 break
107 }
108 }
109
danielk197720713f32007-05-03 11:43:33 +0000110 db close
111 file delete -force test.db test.db-journal
112
113 sqlite3 db test.db
114 execsql "PRAGMA auto_vacuum = $AutoVacuumMode"
115
116 do_test incrblob-2.$AutoVacuumMode.1 {
117 set ::str [string repeat abcdefghij 2900]
118 execsql {
119 BEGIN;
danielk19778cbadb02007-05-03 16:31:26 +0000120 CREATE TABLE blobs(k PRIMARY KEY, v BLOB, i INTEGER);
danielk197720713f32007-05-03 11:43:33 +0000121 DELETE FROM blobs;
danielk19778cbadb02007-05-03 16:31:26 +0000122 INSERT INTO blobs VALUES('one', $::str || randstr(500,500), 45);
danielk197720713f32007-05-03 11:43:33 +0000123 COMMIT;
124 }
125 expr [file size test.db]/1024
126 } [expr 31 + $AutoVacuumMode]
127
drh847d3ab2007-05-04 14:36:22 +0000128 ifcapable autovacuum {
129 do_test incrblob-2.$AutoVacuumMode.2 {
130 execsql {
131 PRAGMA auto_vacuum;
132 }
133 } $AutoVacuumMode
134 }
danielk197720713f32007-05-03 11:43:33 +0000135
136 do_test incrblob-2.$AutoVacuumMode.3 {
137 # Open and close the db to make sure the page cache is empty.
138 db close
139 sqlite3 db test.db
140
141 # Read the last 20 bytes of the blob via a blob handle.
142 set ::blob [db incrblob blobs v 1]
143 seek $::blob -20 end
144 set ::fragment [read $::blob]
145 close $::blob
146
147 # If the database is not in auto-vacuum mode, the whole of
148 # the overflow-chain must be scanned. In auto-vacuum mode,
149 # sqlite uses the ptrmap pages to avoid reading the other pages.
150 #
151 nRead db
152 } [expr $AutoVacuumMode ? 4 : 30]
153
danielk19778cbadb02007-05-03 16:31:26 +0000154 do_test incrblob-2.$AutoVacuumMode.4 {
danielk197720713f32007-05-03 11:43:33 +0000155 string range [db one {SELECT v FROM blobs}] end-19 end
156 } $::fragment
danielk19778cbadb02007-05-03 16:31:26 +0000157
158 do_test incrblob-2.$AutoVacuumMode.5 {
159 # Open and close the db to make sure the page cache is empty.
160 db close
161 sqlite3 db test.db
162
163 # Write the second-to-last 20 bytes of the blob via a blob handle.
164 #
165 set ::blob [db incrblob blobs v 1]
166 seek $::blob -40 end
167 puts -nonewline $::blob "1234567890abcdefghij"
168 flush $::blob
169
170 # If the database is not in auto-vacuum mode, the whole of
171 # the overflow-chain must be scanned. In auto-vacuum mode,
172 # sqlite uses the ptrmap pages to avoid reading the other pages.
173 #
174 nRead db
175 } [expr $AutoVacuumMode ? 4 : 30]
176
177 # Pages 1 (the write-counter) and 32 (the blob data) were written.
178 do_test incrblob-2.$AutoVacuumMode.6 {
179 close $::blob
180 nWrite db
181 } 2
182
183 do_test incrblob-2.$AutoVacuumMode.7 {
184 string range [db one {SELECT v FROM blobs}] end-39 end-20
185 } "1234567890abcdefghij"
186
187 do_test incrblob-2.$AutoVacuumMode.8 {
188 # Open and close the db to make sure the page cache is empty.
189 db close
190 sqlite3 db test.db
191
192 execsql { SELECT i FROM blobs }
193 } {45}
194
195 do_test incrblob-2.$AutoVacuumMode.9 {
196 nRead db
197 } [expr $AutoVacuumMode ? 4 : 30]
danielk197720713f32007-05-03 11:43:33 +0000198}
199
danielk19778cbadb02007-05-03 16:31:26 +0000200#------------------------------------------------------------------------
201# incrblob-3.*:
202#
203# Test the outcome of trying to write to a read-only blob handle.
204#
205# TODO: The following test only tests the tcl interface, not the
206# underlying sqlite3 interface. Need to find some other method
207# to call sqlite3_blob_write() on a readonly handle...
208#
209do_test incrblob-3.1 {
210 set ::blob [db incrblob -readonly blobs v 1]
211 seek $::blob -40 end
212 read $::blob 20
213} "1234567890abcdefghij"
214do_test incrblob-3.2 {
215 seek $::blob 0
216 set rc [catch {
217 puts -nonewline $::blob "helloworld"
218 } msg]
danielk1977f1819242007-05-03 18:14:10 +0000219 close $::blob
danielk19778cbadb02007-05-03 16:31:26 +0000220 list $rc $msg
221} "1 {channel \"$::blob\" wasn't opened for writing}"
222
223#------------------------------------------------------------------------
224# incrblob-4.*:
225#
226# Try a couple of error conditions:
227#
228# 4.1 - Attempt to open a row that does not exist.
229# 4.2 - Attempt to open a column that does not exist.
230# 4.3 - Attempt to open a table that does not exist.
231# 4.4 - Attempt to open a database that does not exist.
232#
danielk1977f1819242007-05-03 18:14:10 +0000233# 4.5 - Attempt to open an integer
234# 4.6 - Attempt to open a real value
235# 4.7 - Attempt to open an SQL null
236#
237# 4.8 - Attempt to open an indexed column for writing
238# 4.9 - Attempt to open an indexed column for reading (this works)
239#
danielk19778cbadb02007-05-03 16:31:26 +0000240do_test incrblob-4.1 {
241 set rc [catch {
242 set ::blob [db incrblob blobs v 2]
243 } msg ]
244 list $rc $msg
245} {1 {no such rowid: 2}}
danielk19778cbadb02007-05-03 16:31:26 +0000246do_test incrblob-4.2 {
247 set rc [catch {
248 set ::blob [db incrblob blobs blue 1]
249 } msg ]
250 list $rc $msg
251} {1 {no such column: "blue"}}
danielk19778cbadb02007-05-03 16:31:26 +0000252do_test incrblob-4.3 {
253 set rc [catch {
254 set ::blob [db incrblob nosuchtable blue 1]
danielk1977f1819242007-05-03 18:14:10 +0000255 } msg ]
danielk19778cbadb02007-05-03 16:31:26 +0000256 list $rc $msg
257} {1 {no such table: main.nosuchtable}}
danielk19778cbadb02007-05-03 16:31:26 +0000258do_test incrblob-4.4 {
259 set rc [catch {
260 set ::blob [db incrblob nosuchdb blobs v 1]
261 } msg ]
262 list $rc $msg
263} {1 {no such table: nosuchdb.blobs}}
264
danielk1977f1819242007-05-03 18:14:10 +0000265do_test incrblob-4.5 {
266 set rc [catch {
267 set ::blob [db incrblob blobs i 1]
268 } msg ]
269 list $rc $msg
270} {1 {cannot open value of type integer}}
271do_test incrblob-4.6 {
272 execsql {
273 INSERT INTO blobs(k, v, i) VALUES(123, 567.765, NULL);
274 }
275 set rc [catch {
276 set ::blob [db incrblob blobs v 2]
277 } msg ]
278 list $rc $msg
279} {1 {cannot open value of type real}}
280do_test incrblob-4.7 {
281 set rc [catch {
282 set ::blob [db incrblob blobs i 2]
283 } msg ]
284 list $rc $msg
285} {1 {cannot open value of type null}}
danielk19778cbadb02007-05-03 16:31:26 +0000286
danielk1977f1819242007-05-03 18:14:10 +0000287do_test incrblob-4.8 {
288 execsql {
289 INSERT INTO blobs(k, v, i) VALUES(X'010203040506070809', 'hello', 'world');
290 }
291 set rc [catch {
292 set ::blob [db incrblob blobs k 3]
293 } msg ]
294 list $rc $msg
295} {1 {cannot open indexed column for writing}}
296
297do_test incrblob-4.9.1 {
298 set rc [catch {
299 set ::blob [db incrblob -readonly blobs k 3]
300 } msg]
301} {0}
302do_test incrblob-4.9.2 {
303 binary scan [read $::blob] c* c
304 close $::blob
305 set c
306} {1 2 3 4 5 6 7 8 9}
307
308#------------------------------------------------------------------------
309# incrblob-5.*:
310#
311# Test that opening a blob in an attached database works.
312#
313do_test incrblob-5.1 {
314 file delete -force test2.db test2.db-journal
315 set ::size [expr [file size [info script]]]
316 execsql {
317 ATTACH 'test2.db' AS aux;
318 CREATE TABLE aux.files(name, text);
319 INSERT INTO aux.files VALUES('this one', zeroblob($::size));
320 }
321 set fd [db incrblob aux files text 1]
322 set fd2 [open [info script]]
323 puts -nonewline $fd [read $fd2]
324 close $fd
325 close $fd2
326 set ::text [db one {select text from aux.files}]
327 string length $::text
328} [file size [info script]]
329do_test incrblob-5.2 {
330 set fd2 [open [info script]]
331 set ::data [read $fd2]
332 close $fd2
333 set ::data
334} $::text
335
336# free memory
337unset ::data
338unset ::text
339
340#------------------------------------------------------------------------
341# incrblob-6.*:
342#
343# Test that opening a blob for write-access is impossible if
344# another connection has the database RESERVED lock.
345#
346# Then test that blob writes that take place inside of a
347# transaction are not visible to external connections until
348# after the transaction is commited and the blob channel
349# closed.
350#
351do_test incrblob-6.1 {
352 sqlite3 db2 test.db
353 execsql {
354 BEGIN;
355 INSERT INTO blobs(k, v, i) VALUES('a', 'different', 'connection');
356 } db2
357} {}
358do_test incrblob-6.2 {
359 execsql {
360 SELECT rowid FROM blobs
361 }
362} {1 2 3}
363do_test incrblob-6.3 {
364 set rc [catch {
365 db incrblob blobs v 1
366 } msg]
367 list $rc $msg
368} {1 {database is locked}}
369do_test incrblob-6.4 {
370 set rc [catch {
371 db incrblob blobs v 3
372 } msg]
373 list $rc $msg
374} {1 {database is locked}}
375do_test incrblob-6.5 {
376 set ::blob [db incrblob -readonly blobs v 3]
377 read $::blob
378} {hello}
379do_test incrblob-6.6 {
380 close $::blob
381} {}
382
383do_test incrblob-6.7 {
384 set ::blob [db2 incrblob blobs i 4]
385 gets $::blob
386} {connection}
387do_test incrblob-6.8 {
388 tell $::blob
389} {10}
390breakpoint
391do_test incrblob-6.9 {
392 seek $::blob 0
393 puts -nonewline $::blob "invocation"
394 flush $::blob
395} {}
396
397# At this point rollback or commit should be illegal (because
398# there is an open blob channel).
399do_test incrblob-6.10 {
400 catchsql {
401 ROLLBACK;
402 } db2
403} {1 {cannot rollback transaction - SQL statements in progress}}
404do_test incrblob-6.11 {
405 catchsql {
406 COMMIT;
407 } db2
408} {1 {cannot commit transaction - SQL statements in progress}}
409
410do_test incrblob-6.12 {
411 execsql {
412 SELECT * FROM blobs WHERE rowid = 4;
413 }
414} {}
415do_test incrblob-6.13 {
416 close $::blob
417 execsql {
418 COMMIT;
419 } db2
420} {}
421do_test incrblob-6.14 {
422 execsql {
423 SELECT * FROM blobs WHERE rowid = 4;
424 }
425} {a different invocation}
426db2 close
427
428finish_test