| 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   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 $soft_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 { |  | 
|  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  |  | 
|  322 do_test incrblob-4.9.1 { |  | 
|  323   set rc [catch { |  | 
|  324     set ::blob [db incrblob -readonly blobs k 3] |  | 
|  325   } msg] |  | 
|  326 } {0} |  | 
|  327 do_test incrblob-4.9.2 { |  | 
|  328   binary scan [read $::blob] c* c |  | 
|  329   close $::blob |  | 
|  330   set c |  | 
|  331 } {1 2 3 4 5 6 7 8 9} |  | 
|  332  |  | 
|  333 do_test incrblob-4.10 { |  | 
|  334   set ::blob [db incrblob -readonly blobs k 3] |  | 
|  335   set rc [catch { sqlite3_blob_read $::blob 10 100 } msg] |  | 
|  336   list $rc $msg |  | 
|  337 } {1 SQLITE_ERROR} |  | 
|  338 do_test incrblob-4.10.2 { |  | 
|  339   close $::blob |  | 
|  340 } {} |  | 
|  341  |  | 
|  342 ifcapable view { |  | 
|  343   do_test incrblob-4.11 { |  | 
|  344     execsql { CREATE VIEW blobs_view AS SELECT k, v, i FROM blobs } |  | 
|  345     set rc [catch { db incrblob blobs_view v 3 } msg] |  | 
|  346     list $rc $msg |  | 
|  347   } {1 {cannot open view: blobs_view}} |  | 
|  348 } |  | 
|  349 ifcapable vtab { |  | 
|  350   register_echo_module [sqlite3_connection_pointer db] |  | 
|  351   do_test incrblob-4.12 { |  | 
|  352     execsql { CREATE VIRTUAL TABLE blobs_echo USING echo(blobs) } |  | 
|  353     set rc [catch { db incrblob blobs_echo v 3 } msg] |  | 
|  354     list $rc $msg |  | 
|  355   } {1 {cannot open virtual table: blobs_echo}} |  | 
|  356 } |  | 
|  357  |  | 
|  358  |  | 
|  359 #------------------------------------------------------------------------ |  | 
|  360 # incrblob-5.*:  |  | 
|  361 # |  | 
|  362 #     Test that opening a blob in an attached database works. |  | 
|  363 # |  | 
|  364 ifcapable attach { |  | 
|  365   do_test incrblob-5.1 { |  | 
|  366     file delete -force test2.db test2.db-journal |  | 
|  367     set ::size [expr [file size [info script]]] |  | 
|  368     execsql { |  | 
|  369       ATTACH 'test2.db' AS aux; |  | 
|  370       CREATE TABLE aux.files(name, text); |  | 
|  371       INSERT INTO aux.files VALUES('this one', zeroblob($::size)); |  | 
|  372     } |  | 
|  373     set fd  [db incrblob aux files text 1] |  | 
|  374     fconfigure $fd -translation binary |  | 
|  375     set fd2 [open [info script]] |  | 
|  376     fconfigure $fd2 -translation binary |  | 
|  377     puts -nonewline $fd [read $fd2] |  | 
|  378     close $fd |  | 
|  379     close $fd2 |  | 
|  380     set ::text [db one {select text from aux.files}] |  | 
|  381     string length $::text |  | 
|  382   } [file size [info script]] |  | 
|  383   do_test incrblob-5.2 { |  | 
|  384     set fd2 [open [info script]] |  | 
|  385     fconfigure $fd2 -translation binary |  | 
|  386     set ::data [read $fd2] |  | 
|  387     close $fd2 |  | 
|  388     set ::data |  | 
|  389   } $::text |  | 
|  390 } |  | 
|  391  |  | 
|  392 # free memory |  | 
|  393 unset -nocomplain ::data |  | 
|  394 unset -nocomplain ::text |  | 
|  395  |  | 
|  396 #------------------------------------------------------------------------ |  | 
|  397 # incrblob-6.*:  |  | 
|  398 # |  | 
|  399 #     Test that opening a blob for write-access is impossible if |  | 
|  400 #     another connection has the database RESERVED lock. |  | 
|  401 # |  | 
|  402 #     Then test that blob writes that take place inside of a |  | 
|  403 #     transaction are not visible to external connections until |  | 
|  404 #     after the transaction is commited and the blob channel  |  | 
|  405 #     closed. |  | 
|  406 # |  | 
|  407 sqlite3_soft_heap_limit 0 |  | 
|  408 do_test incrblob-6.1 { |  | 
|  409   sqlite3 db2 test.db |  | 
|  410   execsql { |  | 
|  411     BEGIN; |  | 
|  412     INSERT INTO blobs(k, v, i) VALUES('a', 'different', 'connection'); |  | 
|  413   } db2 |  | 
|  414 } {} |  | 
|  415 do_test incrblob-6.2 { |  | 
|  416   execsql { |  | 
|  417     SELECT rowid FROM blobs |  | 
|  418   } |  | 
|  419 } {1 2 3} |  | 
|  420 do_test incrblob-6.3 { |  | 
|  421   set rc [catch { |  | 
|  422     db incrblob blobs v 1 |  | 
|  423   } msg] |  | 
|  424   list $rc $msg |  | 
|  425 } {1 {database is locked}} |  | 
|  426 do_test incrblob-6.4 { |  | 
|  427   set rc [catch { |  | 
|  428     db incrblob blobs v 3 |  | 
|  429   } msg] |  | 
|  430   list $rc $msg |  | 
|  431 } {1 {database is locked}} |  | 
|  432 do_test incrblob-6.5 { |  | 
|  433   set ::blob [db incrblob -readonly blobs v 3] |  | 
|  434   read $::blob |  | 
|  435 } {hello} |  | 
|  436 do_test incrblob-6.6 { |  | 
|  437   close $::blob |  | 
|  438 } {} |  | 
|  439  |  | 
|  440 do_test incrblob-6.7 { |  | 
|  441   set ::blob [db2 incrblob blobs i 4] |  | 
|  442   gets $::blob |  | 
|  443 } {connection} |  | 
|  444 do_test incrblob-6.8 { |  | 
|  445   tell $::blob |  | 
|  446 } {10} |  | 
|  447 do_test incrblob-6.9 { |  | 
|  448   seek $::blob 0 |  | 
|  449   puts -nonewline $::blob "invocation" |  | 
|  450   flush $::blob |  | 
|  451 } {} |  | 
|  452  |  | 
|  453 # At this point rollback should be illegal (because  |  | 
|  454 # there is an open blob channel).  But commit is also illegal because |  | 
|  455 # the open blob is read-write. |  | 
|  456 # |  | 
|  457 do_test incrblob-6.10 { |  | 
|  458   catchsql { |  | 
|  459     ROLLBACK; |  | 
|  460   } db2 |  | 
|  461 } {1 {cannot rollback transaction - SQL statements in progress}} |  | 
|  462 do_test incrblob-6.11 { |  | 
|  463   catchsql { |  | 
|  464     COMMIT; |  | 
|  465   } db2 |  | 
|  466 } {1 {cannot commit transaction - SQL statements in progress}} |  | 
|  467  |  | 
|  468 do_test incrblob-6.12 { |  | 
|  469   execsql { |  | 
|  470     SELECT * FROM blobs WHERE rowid = 4; |  | 
|  471   } |  | 
|  472 } {} |  | 
|  473 do_test incrblob-6.13 { |  | 
|  474   close $::blob |  | 
|  475 } {} |  | 
|  476 do_test incrblob-6.14 { |  | 
|  477   catchsql { |  | 
|  478     COMMIT; |  | 
|  479   } db2 |  | 
|  480 } {0 {}} |  | 
|  481 do_test incrblob-6.15 { |  | 
|  482   execsql { |  | 
|  483     SELECT * FROM blobs WHERE rowid = 4; |  | 
|  484   } |  | 
|  485 } {a different invocation} |  | 
|  486 db2 close |  | 
|  487 sqlite3_soft_heap_limit $soft_limit |  | 
|  488  |  | 
|  489 #----------------------------------------------------------------------- |  | 
|  490 # The following tests verify the behaviour of the incremental IO |  | 
|  491 # APIs in the following cases: |  | 
|  492 # |  | 
|  493 #     7.1 A row that containing an open blob is modified. |  | 
|  494 # |  | 
|  495 #     7.2 A CREATE TABLE requires that an overflow page that is part |  | 
|  496 #         of an open blob is moved. |  | 
|  497 # |  | 
|  498 #     7.3 An INCREMENTAL VACUUM moves an overflow page that is part |  | 
|  499 #         of an open blob. |  | 
|  500 # |  | 
|  501 # In the first case above, correct behaviour is for all subsequent |  | 
|  502 # read/write operations on the blob-handle to return SQLITE_ABORT. |  | 
|  503 # More accurately, blob-handles are invalidated whenever the table |  | 
|  504 # they belong to is written to. |  | 
|  505 # |  | 
|  506 # The second two cases have no external effect. They are testing |  | 
|  507 # that the internal cache of overflow page numbers is correctly |  | 
|  508 # invalidated. |  | 
|  509 # |  | 
|  510 do_test incrblob-7.1.0 { |  | 
|  511   execsql { |  | 
|  512     BEGIN; |  | 
|  513     DROP TABLE blobs; |  | 
|  514     CREATE TABLE t1 (a, b, c, d BLOB); |  | 
|  515     INSERT INTO t1(a, b, c, d) VALUES(1, 2, 3, 4); |  | 
|  516     COMMIT; |  | 
|  517   } |  | 
|  518 } {} |  | 
|  519  |  | 
|  520 foreach {tn arg} {1 "" 2 -readonly} { |  | 
|  521  |  | 
|  522   execsql { |  | 
|  523     UPDATE t1 SET d = zeroblob(10000); |  | 
|  524   } |  | 
|  525  |  | 
|  526   do_test incrblob-7.1.$tn.1 { |  | 
|  527     set ::b [eval db incrblob $arg t1 d 1] |  | 
|  528     binary scan [sqlite3_blob_read $::b 5000 5] c* c |  | 
|  529     set c |  | 
|  530   } {0 0 0 0 0} |  | 
|  531   do_test incrblob-7.1.$tn.2 { |  | 
|  532     execsql { |  | 
|  533       UPDATE t1 SET d = 15; |  | 
|  534     } |  | 
|  535   } {} |  | 
|  536   do_test incrblob-7.1.$tn.3 { |  | 
|  537     set rc [catch { sqlite3_blob_read $::b 5000 5 } msg] |  | 
|  538     list $rc $msg |  | 
|  539   } {1 SQLITE_ABORT} |  | 
|  540   do_test incrblob-7.1.$tn.4 { |  | 
|  541     execsql { |  | 
|  542       SELECT d FROM t1; |  | 
|  543     } |  | 
|  544   } {15} |  | 
|  545   do_test incrblob-7.1.$tn.5 { |  | 
|  546     set rc [catch { close $::b } msg] |  | 
|  547     list $rc $msg |  | 
|  548   } {0 {}} |  | 
|  549   do_test incrblob-7.1.$tn.6 { |  | 
|  550     execsql { |  | 
|  551       SELECT d FROM t1; |  | 
|  552     } |  | 
|  553   } {15} |  | 
|  554  |  | 
|  555 } |  | 
|  556  |  | 
|  557 set fd [open [info script]] |  | 
|  558 fconfigure $fd -translation binary |  | 
|  559 set ::data [read $fd 14000] |  | 
|  560 close $fd |  | 
|  561  |  | 
|  562 db close |  | 
|  563 file delete -force test.db test.db-journal |  | 
|  564 sqlite3 db test.db |  | 
|  565  |  | 
|  566 do_test incrblob-7.2.1 { |  | 
|  567   execsql { |  | 
|  568     PRAGMA auto_vacuum = "incremental"; |  | 
|  569     CREATE TABLE t1(a INTEGER PRIMARY KEY, b);        -- root@page3 |  | 
|  570     INSERT INTO t1 VALUES(123, $::data); |  | 
|  571   } |  | 
|  572   set ::b [db incrblob -readonly t1 b 123] |  | 
|  573   fconfigure $::b -translation binary |  | 
|  574   read $::b |  | 
|  575 } $::data |  | 
|  576 do_test incrblob-7.2.2 { |  | 
|  577   execsql { |  | 
|  578     CREATE TABLE t2(a INTEGER PRIMARY KEY, b);        -- root@page4 |  | 
|  579   } |  | 
|  580   seek $::b 0 |  | 
|  581   read $::b |  | 
|  582 } $::data |  | 
|  583 do_test incrblob-7.2.3 { |  | 
|  584   close $::b |  | 
|  585   execsql { |  | 
|  586     SELECT rootpage FROM sqlite_master; |  | 
|  587   } |  | 
|  588 } {3 4} |  | 
|  589  |  | 
|  590 set ::otherdata "[string range $::data 0 1000][string range $::data 1001 end]" |  | 
|  591 do_test incrblob-7.3.1 { |  | 
|  592   execsql { |  | 
|  593     INSERT INTO t2 VALUES(456, $::otherdata); |  | 
|  594   } |  | 
|  595   set ::b [db incrblob -readonly t2 b 456] |  | 
|  596   fconfigure $::b -translation binary |  | 
|  597   read $::b |  | 
|  598 } $::otherdata |  | 
|  599 do_test incrblob-7.3.2 { |  | 
|  600   expr [file size test.db]/1024 |  | 
|  601 } 30 |  | 
|  602 do_test incrblob-7.3.3 { |  | 
|  603   execsql { |  | 
|  604     DELETE FROM t1 WHERE a = 123; |  | 
|  605     PRAGMA INCREMENTAL_VACUUM(0); |  | 
|  606   } |  | 
|  607   seek $::b 0 |  | 
|  608   read $::b |  | 
|  609 } $::otherdata |  | 
|  610  |  | 
|  611 # Attempt to write on a read-only blob.  Make sure the error code |  | 
|  612 # gets set.  Ticket #2464. |  | 
|  613 # |  | 
|  614 do_test incrblob-7.4 { |  | 
|  615   set rc [catch {sqlite3_blob_write $::b 10 HELLO} msg] |  | 
|  616   lappend rc $msg |  | 
|  617 } {1 SQLITE_READONLY} |  | 
|  618 do_test incrblob-7.5 { |  | 
|  619   sqlite3_errcode db |  | 
|  620 } {SQLITE_READONLY} |  | 
|  621 do_test incrblob-7.6 { |  | 
|  622   sqlite3_errmsg db |  | 
|  623 } {attempt to write a readonly database} |  | 
|  624  |  | 
|  625 # Test that if either the "offset" or "amount" arguments to |  | 
|  626 # sqlite3_blob_write() are less than zero, SQLITE_ERROR is returned. |  | 
|  627 #  |  | 
|  628 do_test incrblob-8.1 { |  | 
|  629   execsql { INSERT INTO t1 VALUES(314159, 'sqlite') } |  | 
|  630   set ::b [db incrblob t1 b 314159] |  | 
|  631   fconfigure $::b -translation binary |  | 
|  632   set rc [catch {sqlite3_blob_write $::b 10 HELLO -1} msg] |  | 
|  633   lappend rc $msg |  | 
|  634 } {1 SQLITE_ERROR} |  | 
|  635 do_test incrblob-8.2 { |  | 
|  636   sqlite3_errcode db |  | 
|  637 } {SQLITE_ERROR} |  | 
|  638 do_test incrblob-8.3 { |  | 
|  639   set rc [catch {sqlite3_blob_write $::b -1 HELLO 5} msg] |  | 
|  640   lappend rc $msg |  | 
|  641 } {1 SQLITE_ERROR} |  | 
|  642 do_test incrblob-8.4 { |  | 
|  643   sqlite3_errcode db |  | 
|  644 } {SQLITE_ERROR} |  | 
|  645 do_test incrblob-8.5 { |  | 
|  646   execsql {SELECT b FROM t1 WHERE a = 314159} |  | 
|  647 } {sqlite} |  | 
|  648 do_test incrblob-8.6 { |  | 
|  649   set rc [catch {sqlite3_blob_write $::b 0 etilqs 6} msg] |  | 
|  650   lappend rc $msg |  | 
|  651 } {0 {}} |  | 
|  652 do_test incrblob-8.7 { |  | 
|  653   execsql {SELECT b FROM t1 WHERE a = 314159} |  | 
|  654 } {etilqs} |  | 
|  655  |  | 
|  656  |  | 
|  657 finish_test |  | 
| OLD | NEW |