1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/os/persistentdata/persistentstorage/sqlite3api/TEST/TclScript/incrblob.test Fri Jun 15 03:10:57 2012 +0200
1.3 @@ -0,0 +1,621 @@
1.4 +# 2007 May 1
1.5 +#
1.6 +# The author disclaims copyright to this source code. In place of
1.7 +# a legal notice, here is a blessing:
1.8 +#
1.9 +# May you do good and not evil.
1.10 +# May you find forgiveness for yourself and forgive others.
1.11 +# May you share freely, never taking more than you give.
1.12 +#
1.13 +#***********************************************************************
1.14 +#
1.15 +# $Id: incrblob.test,v 1.21 2008/09/11 11:28:00 danielk1977 Exp $
1.16 +#
1.17 +
1.18 +set testdir [file dirname $argv0]
1.19 +source $testdir/tester.tcl
1.20 +
1.21 +ifcapable {!autovacuum || !pragma || !incrblob} {
1.22 + finish_test
1.23 + return
1.24 +}
1.25 +
1.26 +do_test incrblob-1.1 {
1.27 + execsql {
1.28 + CREATE TABLE blobs(k PRIMARY KEY, v BLOB);
1.29 + INSERT INTO blobs VALUES('one', X'0102030405060708090A');
1.30 + INSERT INTO blobs VALUES('two', X'0A090807060504030201');
1.31 + }
1.32 +} {}
1.33 +
1.34 +do_test incrblob-1.2.1 {
1.35 + set ::blob [db incrblob blobs v 1]
1.36 + string match incrblob_* $::blob
1.37 +} {1}
1.38 +unset -nocomplain data
1.39 +do_test incrblob-1.2.2 {
1.40 + binary scan [read $::blob] c* data
1.41 + set data
1.42 +} {1 2 3 4 5 6 7 8 9 10}
1.43 +do_test incrblob-1.2.3 {
1.44 + seek $::blob 0
1.45 + puts -nonewline $::blob "1234567890"
1.46 + flush $::blob
1.47 +} {}
1.48 +do_test incrblob-1.2.4 {
1.49 + seek $::blob 0
1.50 + binary scan [read $::blob] c* data
1.51 + set data
1.52 +} {49 50 51 52 53 54 55 56 57 48}
1.53 +do_test incrblob-1.2.5 {
1.54 + close $::blob
1.55 +} {}
1.56 +do_test incrblob-1.2.6 {
1.57 + execsql {
1.58 + SELECT v FROM blobs WHERE rowid = 1;
1.59 + }
1.60 +} {1234567890}
1.61 +
1.62 +#--------------------------------------------------------------------
1.63 +# Test cases incrblob-1.3.X check that it is possible to read and write
1.64 +# regions of a blob that lie on overflow pages.
1.65 +#
1.66 +do_test incrblob-1.3.1 {
1.67 + set ::str "[string repeat . 10000]"
1.68 + execsql {
1.69 + INSERT INTO blobs(rowid, k, v) VALUES(3, 'three', $::str);
1.70 + }
1.71 +} {}
1.72 +
1.73 +do_test incrblob-1.3.2 {
1.74 + set ::blob [db incrblob blobs v 3]
1.75 + seek $::blob 8500
1.76 + read $::blob 10
1.77 +} {..........}
1.78 +do_test incrblob-1.3.3 {
1.79 + seek $::blob 8500
1.80 + puts -nonewline $::blob 1234567890
1.81 +} {}
1.82 +do_test incrblob-1.3.4 {
1.83 + seek $::blob 8496
1.84 + read $::blob 10
1.85 +} {....123456}
1.86 +do_test incrblob-1.3.10 {
1.87 + close $::blob
1.88 +} {}
1.89 +
1.90 +#------------------------------------------------------------------------
1.91 +# incrblob-2.*:
1.92 +#
1.93 +# Test that the following operations use ptrmap pages to reduce
1.94 +# unnecessary reads:
1.95 +#
1.96 +# * Reading near the end of a blob,
1.97 +# * Writing near the end of a blob, and
1.98 +# * SELECT a column value that is located on an overflow page.
1.99 +#
1.100 +proc nRead {db} {
1.101 + set bt [btree_from_db $db]
1.102 + db_enter $db
1.103 + array set stats [btree_pager_stats $bt]
1.104 + db_leave $db
1.105 + return $stats(read)
1.106 +}
1.107 +proc nWrite {db} {
1.108 + set bt [btree_from_db $db]
1.109 + db_enter $db
1.110 + array set stats [btree_pager_stats $bt]
1.111 + db_leave $db
1.112 + return $stats(write)
1.113 +}
1.114 +
1.115 +sqlite3_soft_heap_limit 0
1.116 +
1.117 +foreach AutoVacuumMode [list 0 1] {
1.118 +
1.119 + if {$AutoVacuumMode>0} {
1.120 + ifcapable !autovacuum {
1.121 + break
1.122 + }
1.123 + }
1.124 +
1.125 + db close
1.126 + file delete -force test.db test.db-journal
1.127 +
1.128 + sqlite3 db test.db
1.129 + execsql "PRAGMA auto_vacuum = $AutoVacuumMode"
1.130 +
1.131 + do_test incrblob-2.$AutoVacuumMode.1 {
1.132 + set ::str [string repeat abcdefghij 2900]
1.133 + execsql {
1.134 + BEGIN;
1.135 + CREATE TABLE blobs(k PRIMARY KEY, v BLOB, i INTEGER);
1.136 + DELETE FROM blobs;
1.137 + INSERT INTO blobs VALUES('one', $::str || randstr(500,500), 45);
1.138 + COMMIT;
1.139 + }
1.140 + expr [file size test.db]/1024
1.141 + } [expr 31 + $AutoVacuumMode]
1.142 +
1.143 + ifcapable autovacuum {
1.144 + do_test incrblob-2.$AutoVacuumMode.2 {
1.145 + execsql {
1.146 + PRAGMA auto_vacuum;
1.147 + }
1.148 + } $AutoVacuumMode
1.149 + }
1.150 +
1.151 + do_test incrblob-2.$AutoVacuumMode.3 {
1.152 + # Open and close the db to make sure the page cache is empty.
1.153 + db close
1.154 + sqlite3 db test.db
1.155 +
1.156 + # Read the last 20 bytes of the blob via a blob handle.
1.157 + set ::blob [db incrblob blobs v 1]
1.158 + seek $::blob -20 end
1.159 + set ::fragment [read $::blob]
1.160 + close $::blob
1.161 +
1.162 + # If the database is not in auto-vacuum mode, the whole of
1.163 + # the overflow-chain must be scanned. In auto-vacuum mode,
1.164 + # sqlite uses the ptrmap pages to avoid reading the other pages.
1.165 + #
1.166 + nRead db
1.167 + } [expr $AutoVacuumMode ? 4 : 30]
1.168 +
1.169 + do_test incrblob-2.$AutoVacuumMode.4 {
1.170 + string range [db one {SELECT v FROM blobs}] end-19 end
1.171 + } $::fragment
1.172 +
1.173 + do_test incrblob-2.$AutoVacuumMode.5 {
1.174 + # Open and close the db to make sure the page cache is empty.
1.175 + db close
1.176 + sqlite3 db test.db
1.177 +
1.178 + # Write the second-to-last 20 bytes of the blob via a blob handle.
1.179 + #
1.180 + set ::blob [db incrblob blobs v 1]
1.181 + seek $::blob -40 end
1.182 + puts -nonewline $::blob "1234567890abcdefghij"
1.183 + flush $::blob
1.184 +
1.185 + # If the database is not in auto-vacuum mode, the whole of
1.186 + # the overflow-chain must be scanned. In auto-vacuum mode,
1.187 + # sqlite uses the ptrmap pages to avoid reading the other pages.
1.188 + #
1.189 + nRead db
1.190 + } [expr $AutoVacuumMode ? 4 : 30]
1.191 +
1.192 + # Pages 1 (the write-counter) and 32 (the blob data) were written.
1.193 + do_test incrblob-2.$AutoVacuumMode.6 {
1.194 + close $::blob
1.195 + nWrite db
1.196 + } 2
1.197 +
1.198 + do_test incrblob-2.$AutoVacuumMode.7 {
1.199 + string range [db one {SELECT v FROM blobs}] end-39 end-20
1.200 + } "1234567890abcdefghij"
1.201 +
1.202 + do_test incrblob-2.$AutoVacuumMode.8 {
1.203 + # Open and close the db to make sure the page cache is empty.
1.204 + db close
1.205 + sqlite3 db test.db
1.206 +
1.207 + execsql { SELECT i FROM blobs }
1.208 + } {45}
1.209 +
1.210 + do_test incrblob-2.$AutoVacuumMode.9 {
1.211 + nRead db
1.212 + } [expr $AutoVacuumMode ? 4 : 30]
1.213 +}
1.214 +sqlite3_soft_heap_limit $soft_limit
1.215 +
1.216 +#------------------------------------------------------------------------
1.217 +# incrblob-3.*:
1.218 +#
1.219 +# Test the outcome of trying to write to a read-only blob handle.
1.220 +#
1.221 +do_test incrblob-3.1 {
1.222 + set ::blob [db incrblob -readonly blobs v 1]
1.223 + seek $::blob -40 end
1.224 + read $::blob 20
1.225 +} "1234567890abcdefghij"
1.226 +do_test incrblob-3.2 {
1.227 + seek $::blob 0
1.228 + set rc [catch {
1.229 + puts -nonewline $::blob "helloworld"
1.230 + } msg]
1.231 + close $::blob
1.232 + list $rc $msg
1.233 +} "1 {channel \"$::blob\" wasn't opened for writing}"
1.234 +
1.235 +do_test incrblob-3.3 {
1.236 + set ::blob [db incrblob -readonly blobs v 1]
1.237 + seek $::blob -40 end
1.238 + read $::blob 20
1.239 +} "1234567890abcdefghij"
1.240 +do_test incrblob-3.4 {
1.241 + set rc [catch {
1.242 + sqlite3_blob_write $::blob 20 "qwertyuioplkjhgfds"
1.243 + } msg]
1.244 + list $rc $msg
1.245 +} {1 SQLITE_READONLY}
1.246 +catch {close $::blob}
1.247 +
1.248 +#------------------------------------------------------------------------
1.249 +# incrblob-4.*:
1.250 +#
1.251 +# Try a couple of error conditions:
1.252 +#
1.253 +# 4.1 - Attempt to open a row that does not exist.
1.254 +# 4.2 - Attempt to open a column that does not exist.
1.255 +# 4.3 - Attempt to open a table that does not exist.
1.256 +# 4.4 - Attempt to open a database that does not exist.
1.257 +#
1.258 +# 4.5 - Attempt to open an integer
1.259 +# 4.6 - Attempt to open a real value
1.260 +# 4.7 - Attempt to open an SQL null
1.261 +#
1.262 +# 4.8 - Attempt to open an indexed column for writing
1.263 +# 4.9 - Attempt to open an indexed column for reading (this works)
1.264 +#
1.265 +# 4.11 - Attempt to open a column of a view.
1.266 +# 4.12 - Attempt to open a column of a virtual table.
1.267 +#
1.268 +do_test incrblob-4.1 {
1.269 + set rc [catch {
1.270 + set ::blob [db incrblob blobs v 2]
1.271 + } msg ]
1.272 + list $rc $msg
1.273 +} {1 {no such rowid: 2}}
1.274 +do_test incrblob-4.2 {
1.275 + set rc [catch {
1.276 + set ::blob [db incrblob blobs blue 1]
1.277 + } msg ]
1.278 + list $rc $msg
1.279 +} {1 {no such column: "blue"}}
1.280 +do_test incrblob-4.3 {
1.281 + set rc [catch {
1.282 + set ::blob [db incrblob nosuchtable blue 1]
1.283 + } msg ]
1.284 + list $rc $msg
1.285 +} {1 {no such table: main.nosuchtable}}
1.286 +do_test incrblob-4.4 {
1.287 + set rc [catch {
1.288 + set ::blob [db incrblob nosuchdb blobs v 1]
1.289 + } msg ]
1.290 + list $rc $msg
1.291 +} {1 {no such table: nosuchdb.blobs}}
1.292 +
1.293 +do_test incrblob-4.5 {
1.294 + set rc [catch {
1.295 + set ::blob [db incrblob blobs i 1]
1.296 + } msg ]
1.297 + list $rc $msg
1.298 +} {1 {cannot open value of type integer}}
1.299 +do_test incrblob-4.6 {
1.300 + execsql {
1.301 + INSERT INTO blobs(k, v, i) VALUES(123, 567.765, NULL);
1.302 + }
1.303 + set rc [catch {
1.304 + set ::blob [db incrblob blobs v 2]
1.305 + } msg ]
1.306 + list $rc $msg
1.307 +} {1 {cannot open value of type real}}
1.308 +do_test incrblob-4.7 {
1.309 + set rc [catch {
1.310 + set ::blob [db incrblob blobs i 2]
1.311 + } msg ]
1.312 + list $rc $msg
1.313 +} {1 {cannot open value of type null}}
1.314 +
1.315 +do_test incrblob-4.8 {
1.316 + execsql {
1.317 + INSERT INTO blobs(k, v, i) VALUES(X'010203040506070809', 'hello', 'world');
1.318 + }
1.319 + set rc [catch {
1.320 + set ::blob [db incrblob blobs k 3]
1.321 + } msg ]
1.322 + list $rc $msg
1.323 +} {1 {cannot open indexed column for writing}}
1.324 +
1.325 +do_test incrblob-4.9.1 {
1.326 + set rc [catch {
1.327 + set ::blob [db incrblob -readonly blobs k 3]
1.328 + } msg]
1.329 +} {0}
1.330 +do_test incrblob-4.9.2 {
1.331 + binary scan [read $::blob] c* c
1.332 + close $::blob
1.333 + set c
1.334 +} {1 2 3 4 5 6 7 8 9}
1.335 +
1.336 +do_test incrblob-4.10 {
1.337 + set ::blob [db incrblob -readonly blobs k 3]
1.338 + set rc [catch { sqlite3_blob_read $::blob 10 100 } msg]
1.339 + list $rc $msg
1.340 +} {1 SQLITE_ERROR}
1.341 +do_test incrblob-4.10.2 {
1.342 + close $::blob
1.343 +} {}
1.344 +
1.345 +ifcapable view {
1.346 + do_test incrblob-4.11 {
1.347 + execsql { CREATE VIEW blobs_view AS SELECT k, v, i FROM blobs }
1.348 + set rc [catch { db incrblob blobs_view v 3 } msg]
1.349 + list $rc $msg
1.350 + } {1 {cannot open view: blobs_view}}
1.351 +}
1.352 +ifcapable vtab {
1.353 + register_echo_module [sqlite3_connection_pointer db]
1.354 + do_test incrblob-4.12 {
1.355 + execsql { CREATE VIRTUAL TABLE blobs_echo USING echo(blobs) }
1.356 + set rc [catch { db incrblob blobs_echo v 3 } msg]
1.357 + list $rc $msg
1.358 + } {1 {cannot open virtual table: blobs_echo}}
1.359 +}
1.360 +
1.361 +
1.362 +#------------------------------------------------------------------------
1.363 +# incrblob-5.*:
1.364 +#
1.365 +# Test that opening a blob in an attached database works.
1.366 +#
1.367 +ifcapable attach {
1.368 + do_test incrblob-5.1 {
1.369 + file delete -force test2.db test2.db-journal
1.370 + set ::size [expr [file size [info script]]]
1.371 + execsql {
1.372 + ATTACH 'test2.db' AS aux;
1.373 + CREATE TABLE aux.files(name, text);
1.374 + INSERT INTO aux.files VALUES('this one', zeroblob($::size));
1.375 + }
1.376 + set fd [db incrblob aux files text 1]
1.377 + fconfigure $fd -translation binary
1.378 + set fd2 [open [info script]]
1.379 + fconfigure $fd2 -translation binary
1.380 + puts -nonewline $fd [read $fd2]
1.381 + close $fd
1.382 + close $fd2
1.383 + set ::text [db one {select text from aux.files}]
1.384 + string length $::text
1.385 + } [file size [info script]]
1.386 + do_test incrblob-5.2 {
1.387 + set fd2 [open [info script]]
1.388 + fconfigure $fd2 -translation binary
1.389 + set ::data [read $fd2]
1.390 + close $fd2
1.391 + set ::data
1.392 + } $::text
1.393 +}
1.394 +
1.395 +# free memory
1.396 +unset -nocomplain ::data
1.397 +unset -nocomplain ::text
1.398 +
1.399 +#------------------------------------------------------------------------
1.400 +# incrblob-6.*:
1.401 +#
1.402 +# Test that opening a blob for write-access is impossible if
1.403 +# another connection has the database RESERVED lock.
1.404 +#
1.405 +# Then test that blob writes that take place inside of a
1.406 +# transaction are not visible to external connections until
1.407 +# after the transaction is commited and the blob channel
1.408 +# closed.
1.409 +#
1.410 +sqlite3_soft_heap_limit 0
1.411 +do_test incrblob-6.1 {
1.412 + sqlite3 db2 test.db
1.413 + execsql {
1.414 + BEGIN;
1.415 + INSERT INTO blobs(k, v, i) VALUES('a', 'different', 'connection');
1.416 + } db2
1.417 +} {}
1.418 +do_test incrblob-6.2 {
1.419 + execsql {
1.420 + SELECT rowid FROM blobs
1.421 + }
1.422 +} {1 2 3}
1.423 +do_test incrblob-6.3 {
1.424 + set rc [catch {
1.425 + db incrblob blobs v 1
1.426 + } msg]
1.427 + list $rc $msg
1.428 +} {1 {database is locked}}
1.429 +do_test incrblob-6.4 {
1.430 + set rc [catch {
1.431 + db incrblob blobs v 3
1.432 + } msg]
1.433 + list $rc $msg
1.434 +} {1 {database is locked}}
1.435 +do_test incrblob-6.5 {
1.436 + set ::blob [db incrblob -readonly blobs v 3]
1.437 + read $::blob
1.438 +} {hello}
1.439 +do_test incrblob-6.6 {
1.440 + close $::blob
1.441 +} {}
1.442 +
1.443 +do_test incrblob-6.7 {
1.444 + set ::blob [db2 incrblob blobs i 4]
1.445 + gets $::blob
1.446 +} {connection}
1.447 +do_test incrblob-6.8 {
1.448 + tell $::blob
1.449 +} {10}
1.450 +do_test incrblob-6.9 {
1.451 + seek $::blob 0
1.452 + puts -nonewline $::blob "invocation"
1.453 + flush $::blob
1.454 +} {}
1.455 +
1.456 +# At this point rollback or commit should be illegal (because
1.457 +# there is an open blob channel).
1.458 +do_test incrblob-6.10 {
1.459 + catchsql {
1.460 + ROLLBACK;
1.461 + } db2
1.462 +} {1 {cannot rollback transaction - SQL statements in progress}}
1.463 +do_test incrblob-6.11 {
1.464 + catchsql {
1.465 + COMMIT;
1.466 + } db2
1.467 +} {1 {cannot commit transaction - SQL statements in progress}}
1.468 +
1.469 +do_test incrblob-6.12 {
1.470 + execsql {
1.471 + SELECT * FROM blobs WHERE rowid = 4;
1.472 + }
1.473 +} {}
1.474 +do_test incrblob-6.13 {
1.475 + close $::blob
1.476 + execsql {
1.477 + COMMIT;
1.478 + } db2
1.479 +} {}
1.480 +do_test incrblob-6.14 {
1.481 + execsql {
1.482 + SELECT * FROM blobs WHERE rowid = 4;
1.483 + }
1.484 +} {a different invocation}
1.485 +db2 close
1.486 +sqlite3_soft_heap_limit $soft_limit
1.487 +
1.488 +#-----------------------------------------------------------------------
1.489 +# The following tests verify the behaviour of the incremental IO
1.490 +# APIs in the following cases:
1.491 +#
1.492 +# 7.1 A row that containing an open blob is modified.
1.493 +#
1.494 +# 7.2 A CREATE TABLE requires that an overflow page that is part
1.495 +# of an open blob is moved.
1.496 +#
1.497 +# 7.3 An INCREMENTAL VACUUM moves an overflow page that is part
1.498 +# of an open blob.
1.499 +#
1.500 +# In the first case above, correct behaviour is for all subsequent
1.501 +# read/write operations on the blob-handle to return SQLITE_ABORT.
1.502 +# More accurately, blob-handles are invalidated whenever the table
1.503 +# they belong to is written to.
1.504 +#
1.505 +# The second two cases have no external effect. They are testing
1.506 +# that the internal cache of overflow page numbers is correctly
1.507 +# invalidated.
1.508 +#
1.509 +do_test incrblob-7.1.0 {
1.510 + execsql {
1.511 + BEGIN;
1.512 + DROP TABLE blobs;
1.513 + CREATE TABLE t1 (a, b, c, d BLOB);
1.514 + INSERT INTO t1(a, b, c, d) VALUES(1, 2, 3, 4);
1.515 + COMMIT;
1.516 + }
1.517 +} {}
1.518 +
1.519 +foreach {tn arg} {1 "" 2 -readonly} {
1.520 +
1.521 + execsql {
1.522 + UPDATE t1 SET d = zeroblob(10000);
1.523 + }
1.524 +
1.525 + do_test incrblob-7.1.$tn.1 {
1.526 + set ::b [eval db incrblob $arg t1 d 1]
1.527 + binary scan [sqlite3_blob_read $::b 5000 5] c* c
1.528 + set c
1.529 + } {0 0 0 0 0}
1.530 + do_test incrblob-7.1.$tn.2 {
1.531 + execsql {
1.532 + UPDATE t1 SET d = 15;
1.533 + }
1.534 + } {}
1.535 + do_test incrblob-7.1.$tn.3 {
1.536 + set rc [catch { sqlite3_blob_read $::b 5000 5 } msg]
1.537 + list $rc $msg
1.538 + } {1 SQLITE_ABORT}
1.539 + do_test incrblob-7.1.$tn.4 {
1.540 + execsql {
1.541 + SELECT d FROM t1;
1.542 + }
1.543 + } {15}
1.544 + do_test incrblob-7.1.$tn.5 {
1.545 + set rc [catch { close $::b } msg]
1.546 + list $rc $msg
1.547 + } {0 {}}
1.548 + do_test incrblob-7.1.$tn.6 {
1.549 + execsql {
1.550 + SELECT d FROM t1;
1.551 + }
1.552 + } {15}
1.553 +
1.554 +}
1.555 +
1.556 +set fd [open [info script]]
1.557 +fconfigure $fd -translation binary
1.558 +set ::data [read $fd 14000]
1.559 +close $fd
1.560 +
1.561 +db close
1.562 +file delete -force test.db test.db-journal
1.563 +sqlite3 db test.db
1.564 +
1.565 +do_test incrblob-7.2.1 {
1.566 + execsql {
1.567 + PRAGMA auto_vacuum = "incremental";
1.568 + CREATE TABLE t1(a INTEGER PRIMARY KEY, b); -- root@page3
1.569 + INSERT INTO t1 VALUES(123, $::data);
1.570 + }
1.571 + set ::b [db incrblob -readonly t1 b 123]
1.572 + fconfigure $::b -translation binary
1.573 + read $::b
1.574 +} $::data
1.575 +do_test incrblob-7.2.2 {
1.576 + execsql {
1.577 + CREATE TABLE t2(a INTEGER PRIMARY KEY, b); -- root@page4
1.578 + }
1.579 + seek $::b 0
1.580 + read $::b
1.581 +} $::data
1.582 +do_test incrblob-7.2.3 {
1.583 + close $::b
1.584 + execsql {
1.585 + SELECT rootpage FROM sqlite_master;
1.586 + }
1.587 +} {3 4}
1.588 +
1.589 +set ::otherdata "[string range $::data 0 1000][string range $::data 1001 end]"
1.590 +do_test incrblob-7.3.1 {
1.591 + execsql {
1.592 + INSERT INTO t2 VALUES(456, $::otherdata);
1.593 + }
1.594 + set ::b [db incrblob -readonly t2 b 456]
1.595 + fconfigure $::b -translation binary
1.596 + read $::b
1.597 +} $::otherdata
1.598 +do_test incrblob-7.3.2 {
1.599 + expr [file size test.db]/1024
1.600 +} 30
1.601 +do_test incrblob-7.3.3 {
1.602 + execsql {
1.603 + DELETE FROM t1 WHERE a = 123;
1.604 + PRAGMA INCREMENTAL_VACUUM(0);
1.605 + }
1.606 + seek $::b 0
1.607 + read $::b
1.608 +} $::otherdata
1.609 +
1.610 +# Attempt to write on a read-only blob. Make sure the error code
1.611 +# gets set. Ticket #2464.
1.612 +#
1.613 +do_test incrblob-7.4 {
1.614 + set rc [catch {sqlite3_blob_write $::b 10 HELLO} msg]
1.615 + lappend rc $msg
1.616 +} {1 SQLITE_READONLY}
1.617 +do_test incrblob-7.5 {
1.618 + sqlite3_errcode db
1.619 +} {SQLITE_READONLY}
1.620 +do_test incrblob-7.6 {
1.621 + sqlite3_errmsg db
1.622 +} {attempt to write a readonly database}
1.623 +
1.624 +finish_test