| OLD | NEW | 
 | (Empty) | 
|    1 # 2008 June 9 |  | 
|    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 # Test that it is possible to have two open blob handles on a single |  | 
|   13 # blob object. |  | 
|   14 # |  | 
|   15 # $Id: incrblob2.test,v 1.11 2009/06/29 06:00:37 danielk1977 Exp $ |  | 
|   16 # |  | 
|   17  |  | 
|   18 set testdir [file dirname $argv0] |  | 
|   19 source $testdir/tester.tcl |  | 
|   20  |  | 
|   21 ifcapable {!autovacuum || !pragma || !incrblob} { |  | 
|   22   finish_test |  | 
|   23   return |  | 
|   24 } |  | 
|   25  |  | 
|   26 do_test incrblob2-1.0 { |  | 
|   27   execsql { |  | 
|   28     CREATE TABLE blobs(id INTEGER PRIMARY KEY, data BLOB); |  | 
|   29     INSERT INTO blobs VALUES(NULL, zeroblob(5000)); |  | 
|   30     INSERT INTO blobs VALUES(NULL, zeroblob(5000)); |  | 
|   31     INSERT INTO blobs VALUES(NULL, zeroblob(5000)); |  | 
|   32     INSERT INTO blobs VALUES(NULL, zeroblob(5000)); |  | 
|   33   } |  | 
|   34 } {} |  | 
|   35  |  | 
|   36 foreach iOffset [list 0 256 4094] { |  | 
|   37   do_test incrblob2-1.$iOffset.1 { |  | 
|   38     set fd [db incrblob blobs data 1] |  | 
|   39     puts $fd "[string repeat x $iOffset]SQLite version 3.6.0" |  | 
|   40     close $fd |  | 
|   41   } {} |  | 
|   42    |  | 
|   43   do_test incrblob2-1.$iOffset.2 { |  | 
|   44     set fd1 [db incrblob blobs data 1] |  | 
|   45     set fd2 [db incrblob blobs data 1] |  | 
|   46     fconfigure $fd1 -buffering none |  | 
|   47     fconfigure $fd2 -buffering none |  | 
|   48     if {$iOffset != 0} { |  | 
|   49       seek $fd2 $iOffset start |  | 
|   50       seek $fd1 $iOffset start |  | 
|   51     } |  | 
|   52     read $fd1 6 |  | 
|   53   } {SQLite} |  | 
|   54    |  | 
|   55   do_test incrblob2-1.$iOffset.3 { |  | 
|   56     read $fd2 6 |  | 
|   57   } {SQLite} |  | 
|   58    |  | 
|   59   do_test incrblob2-1.$iOffset.4 { |  | 
|   60     seek $fd2 $iOffset start |  | 
|   61     seek $fd1 $iOffset start |  | 
|   62     puts -nonewline $fd2 "etiLQS" |  | 
|   63   } {} |  | 
|   64  |  | 
|   65    |  | 
|   66   do_test incrblob2-1.$iOffset.5 { |  | 
|   67     seek $fd1 $iOffset start |  | 
|   68     read $fd1 6 |  | 
|   69   } {etiLQS} |  | 
|   70    |  | 
|   71   do_test incrblob2-1.$iOffset.6 { |  | 
|   72     seek $fd2 $iOffset start |  | 
|   73     read $fd2 6 |  | 
|   74   } {etiLQS} |  | 
|   75    |  | 
|   76   do_test incrblob2-1.$iOffset.7 { |  | 
|   77     seek $fd1 $iOffset start |  | 
|   78     read $fd1 6 |  | 
|   79   } {etiLQS} |  | 
|   80    |  | 
|   81   do_test incrblob2-1.$iOffset.8 { |  | 
|   82     close $fd1 |  | 
|   83     close $fd2 |  | 
|   84   } {} |  | 
|   85 } |  | 
|   86  |  | 
|   87 #-------------------------------------------------------------------------- |  | 
|   88  |  | 
|   89 foreach iOffset [list 0 256 4094] { |  | 
|   90  |  | 
|   91   do_test incrblob2-2.$iOffset.1 { |  | 
|   92     set fd1 [db incrblob blobs data 1] |  | 
|   93     seek $fd1 [expr $iOffset - 5000] end |  | 
|   94     fconfigure $fd1 -buffering none |  | 
|   95  |  | 
|   96     set fd2 [db incrblob blobs data 1] |  | 
|   97     seek $fd2 [expr $iOffset - 5000] end |  | 
|   98     fconfigure $fd2 -buffering none |  | 
|   99  |  | 
|  100     puts -nonewline $fd1 "123456" |  | 
|  101   } {} |  | 
|  102    |  | 
|  103   do_test incrblob2-2.$iOffset.2 { |  | 
|  104     read $fd2 6 |  | 
|  105   } {123456} |  | 
|  106  |  | 
|  107   do_test incrblob2-2.$iOffset.3 { |  | 
|  108     close $fd1 |  | 
|  109     close $fd2 |  | 
|  110   } {} |  | 
|  111 } |  | 
|  112  |  | 
|  113 do_test incrblob2-3.1 { |  | 
|  114   set fd1 [db incrblob blobs data 1] |  | 
|  115   fconfigure $fd1 -buffering none |  | 
|  116 } {} |  | 
|  117 do_test incrblob2-3.2 { |  | 
|  118   execsql { |  | 
|  119     INSERT INTO blobs VALUES(5, zeroblob(10240)); |  | 
|  120   } |  | 
|  121 } {} |  | 
|  122 do_test incrblob2-3.3 { |  | 
|  123   set rc [catch { read $fd1 6 } msg] |  | 
|  124   list $rc $msg |  | 
|  125 } {0 123456} |  | 
|  126 do_test incrblob2-3.4 { |  | 
|  127   close $fd1 |  | 
|  128 } {} |  | 
|  129  |  | 
|  130 #-------------------------------------------------------------------------- |  | 
|  131 # The following tests - incrblob2-4.* - test that blob handles are  |  | 
|  132 # invalidated at the correct times. |  | 
|  133 # |  | 
|  134 do_test incrblob2-4.1 { |  | 
|  135   unset -nocomplain data |  | 
|  136   db eval BEGIN |  | 
|  137   db eval { CREATE TABLE t1(id INTEGER PRIMARY KEY, data BLOB); } |  | 
|  138   for {set ii 1} {$ii < 100} {incr ii} { |  | 
|  139     set data [string repeat "blob$ii" 500] |  | 
|  140     db eval { INSERT INTO t1 VALUES($ii, $data) } |  | 
|  141   } |  | 
|  142   db eval COMMIT |  | 
|  143 } {} |  | 
|  144  |  | 
|  145 proc aborted_handles {} { |  | 
|  146   global handles |  | 
|  147  |  | 
|  148   set aborted {} |  | 
|  149   for {set ii 1} {$ii < 100} {incr ii} { |  | 
|  150     set str "blob$ii" |  | 
|  151     set nByte [string length $str] |  | 
|  152     set iOffset [expr $nByte * $ii * 2] |  | 
|  153  |  | 
|  154     set rc [catch {sqlite3_blob_read $handles($ii) $iOffset $nByte} msg] |  | 
|  155     if {$rc && $msg eq "SQLITE_ABORT"} { |  | 
|  156       lappend aborted $ii |  | 
|  157     } else { |  | 
|  158       if {$rc || $msg ne $str} { |  | 
|  159         error "blob $ii: $msg" |  | 
|  160       } |  | 
|  161     } |  | 
|  162   } |  | 
|  163   set aborted |  | 
|  164 } |  | 
|  165  |  | 
|  166 do_test incrblob2-4.2 { |  | 
|  167   for {set ii 1} {$ii < 100} {incr ii} { |  | 
|  168     set handles($ii) [db incrblob t1 data $ii] |  | 
|  169   } |  | 
|  170   aborted_handles |  | 
|  171 } {} |  | 
|  172  |  | 
|  173 # Update row 3. This should abort handle 3 but leave all others untouched. |  | 
|  174 # |  | 
|  175 do_test incrblob2-4.3 { |  | 
|  176   db eval {UPDATE t1 SET data = data || '' WHERE id = 3} |  | 
|  177   aborted_handles |  | 
|  178 } {3} |  | 
|  179  |  | 
|  180 # Test that a write to handle 3 also returns SQLITE_ABORT. |  | 
|  181 # |  | 
|  182 do_test incrblob2-4.3.1 { |  | 
|  183   set rc [catch {sqlite3_blob_write $::handles(3) 10 HELLO} msg] |  | 
|  184   list $rc $msg |  | 
|  185 } {1 SQLITE_ABORT} |  | 
|  186  |  | 
|  187 # Delete row 14. This should abort handle 6 but leave all others untouched. |  | 
|  188 # |  | 
|  189 do_test incrblob2-4.4 { |  | 
|  190   db eval {DELETE FROM t1 WHERE id = 14} |  | 
|  191   aborted_handles |  | 
|  192 } {3 14} |  | 
|  193  |  | 
|  194 # Change the rowid of row 15 to 102. Should abort handle 15. |  | 
|  195 # |  | 
|  196 do_test incrblob2-4.5 { |  | 
|  197   db eval {UPDATE t1 SET id = 102 WHERE id = 15} |  | 
|  198   aborted_handles |  | 
|  199 } {3 14 15} |  | 
|  200  |  | 
|  201 # Clobber row 92 using INSERT OR REPLACE. |  | 
|  202 # |  | 
|  203 do_test incrblob2-4.6 { |  | 
|  204   db eval {INSERT OR REPLACE INTO t1 VALUES(92, zeroblob(1000))} |  | 
|  205   aborted_handles |  | 
|  206 } {3 14 15 92} |  | 
|  207  |  | 
|  208 # Clobber row 65 using UPDATE OR REPLACE on row 35. This should abort  |  | 
|  209 # handles 35 and 65. |  | 
|  210 # |  | 
|  211 do_test incrblob2-4.7 { |  | 
|  212   db eval {UPDATE OR REPLACE t1 SET id = 65 WHERE id = 35} |  | 
|  213   aborted_handles |  | 
|  214 } {3 14 15 35 65 92} |  | 
|  215  |  | 
|  216 # Insert a couple of new rows. This should not invalidate any handles. |  | 
|  217 # |  | 
|  218 do_test incrblob2-4.9 { |  | 
|  219   db eval {INSERT INTO t1 SELECT NULL, data FROM t1} |  | 
|  220   aborted_handles |  | 
|  221 } {3 14 15 35 65 92} |  | 
|  222  |  | 
|  223 # Delete all rows from 1 to 25. This should abort all handles up to 25. |  | 
|  224 # |  | 
|  225 do_test incrblob2-4.9 { |  | 
|  226   db eval {DELETE FROM t1 WHERE id >=1 AND id <= 25} |  | 
|  227   aborted_handles |  | 
|  228 } {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 35 65 92} |  | 
|  229  |  | 
|  230 # Delete the whole table (this will use sqlite3BtreeClearTable()). All handles |  | 
|  231 # should now be aborted. |  | 
|  232 # |  | 
|  233 do_test incrblob2-4.10 { |  | 
|  234   db eval {DELETE FROM t1} |  | 
|  235   aborted_handles |  | 
|  236 } {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
      30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 5
     6 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 
     83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99} |  | 
|  237  |  | 
|  238 do_test incrblob2-4.1.X { |  | 
|  239   for {set ii 1} {$ii < 100} {incr ii} { |  | 
|  240     close $handles($ii)  |  | 
|  241   } |  | 
|  242 } {} |  | 
|  243  |  | 
|  244 #-------------------------------------------------------------------------- |  | 
|  245 # The following tests - incrblob2-5.* - test that in shared cache an open |  | 
|  246 # blob handle counts as a read-lock on its table. |  | 
|  247 # |  | 
|  248 ifcapable shared_cache { |  | 
|  249   db close |  | 
|  250   set ::enable_shared_cache [sqlite3_enable_shared_cache 1] |  | 
|  251  |  | 
|  252   do_test incrblob2-5.1 { |  | 
|  253     sqlite3 db test.db |  | 
|  254     sqlite3 db2 test.db |  | 
|  255  |  | 
|  256     execsql { |  | 
|  257       INSERT INTO t1 VALUES(1, 'abcde'); |  | 
|  258     } |  | 
|  259   } {} |  | 
|  260  |  | 
|  261   do_test incrblob2-5.2 { |  | 
|  262     catchsql { INSERT INTO t1 VALUES(2, 'fghij') } db2 |  | 
|  263   } {0 {}} |  | 
|  264  |  | 
|  265   do_test incrblob2-5.3 { |  | 
|  266     set blob [db incrblob t1 data 1] |  | 
|  267     catchsql { INSERT INTO t1 VALUES(3, 'klmno') } db2 |  | 
|  268   } {1 {database table is locked}} |  | 
|  269  |  | 
|  270   do_test incrblob2-5.4 { |  | 
|  271     close $blob |  | 
|  272     execsql BEGIN db2 |  | 
|  273     catchsql { INSERT INTO t1 VALUES(4, 'pqrst') } db2 |  | 
|  274   } {0 {}} |  | 
|  275  |  | 
|  276   do_test incrblob2-5.5 { |  | 
|  277     set rc [catch { db incrblob -readonly t1 data 1 } msg] |  | 
|  278     list $rc $msg |  | 
|  279   } {1 {database table is locked: t1}} |  | 
|  280  |  | 
|  281   do_test incrblob2-5.6 { |  | 
|  282     execsql { PRAGMA read_uncommitted=1 } |  | 
|  283     set blob [db incrblob -readonly t1 data 4] |  | 
|  284     read $blob |  | 
|  285   } {pqrst} |  | 
|  286  |  | 
|  287   do_test incrblob2-5.7 { |  | 
|  288     catchsql { INSERT INTO t1 VALUES(3, 'klmno') } db2 |  | 
|  289   } {0 {}} |  | 
|  290  |  | 
|  291   do_test incrblob2-5.8 { |  | 
|  292     close $blob |  | 
|  293   } {} |  | 
|  294  |  | 
|  295   db2 close |  | 
|  296   db close |  | 
|  297   sqlite3_enable_shared_cache $::enable_shared_cache |  | 
|  298 } |  | 
|  299  |  | 
|  300 #-------------------------------------------------------------------------- |  | 
|  301 # The following tests - incrblob2-6.* - test a specific scenario that might |  | 
|  302 # be causing an error. |  | 
|  303 # |  | 
|  304 sqlite3 db test.db |  | 
|  305 do_test incrblob2-6.1 { |  | 
|  306   execsql { |  | 
|  307     DELETE FROM t1; |  | 
|  308     INSERT INTO t1 VALUES(1, zeroblob(100)); |  | 
|  309   } |  | 
|  310    |  | 
|  311   set rdHandle [db incrblob -readonly t1 data 1] |  | 
|  312   set wrHandle [db incrblob t1 data 1] |  | 
|  313  |  | 
|  314   sqlite3_blob_read $rdHandle 0 100 |  | 
|  315  |  | 
|  316   sqlite3_blob_write $wrHandle 0 ABCDEF |  | 
|  317  |  | 
|  318   close $wrHandle |  | 
|  319   close $rdHandle |  | 
|  320 } {} |  | 
|  321  |  | 
|  322 do_test incrblob2-6.2 { |  | 
|  323   set rdHandle [db incrblob -readonly t1 data 1] |  | 
|  324   sqlite3_blob_read $rdHandle 0 2 |  | 
|  325 } {AB} |  | 
|  326  |  | 
|  327 do_test incrblob2-6.3 { |  | 
|  328   set wrHandle [db incrblob t1 data 1] |  | 
|  329   sqlite3_blob_write $wrHandle 0 ZZZZZZZZZZ |  | 
|  330   sqlite3_blob_read $rdHandle 2 4 |  | 
|  331 } {ZZZZ} |  | 
|  332  |  | 
|  333 do_test incrblob2-6.4 { |  | 
|  334   close $wrHandle |  | 
|  335   close $rdHandle |  | 
|  336 } {} |  | 
|  337  |  | 
|  338 sqlite3_memory_highwater 1 |  | 
|  339 do_test incrblob2-7.1 { |  | 
|  340   db eval { |  | 
|  341     CREATE TABLE t2(B BLOB); |  | 
|  342     INSERT INTO t2 VALUES(zeroblob(10 * 1024 * 1024));  |  | 
|  343   } |  | 
|  344   expr {[sqlite3_memory_highwater]<(5 * 1024 * 1024)} |  | 
|  345 } {1} |  | 
|  346  |  | 
|  347 do_test incrblob2-7.2 { |  | 
|  348   set h [db incrblob t2 B 1] |  | 
|  349   expr {[sqlite3_memory_highwater]<(5 * 1024 * 1024)} |  | 
|  350 } {1} |  | 
|  351  |  | 
|  352 do_test incrblob2-7.3 { |  | 
|  353   seek $h 0 end |  | 
|  354   tell $h |  | 
|  355 } [expr 10 * 1024 * 1024] |  | 
|  356  |  | 
|  357 do_test incrblob2-7.4 { |  | 
|  358   expr {[sqlite3_memory_highwater]<(5 * 1024 * 1024)} |  | 
|  359 } {1} |  | 
|  360  |  | 
|  361 do_test incrblob2-7.5 { |  | 
|  362   close $h |  | 
|  363 } {} |  | 
|  364  |  | 
|  365 #--------------------------------------------------------------------------- |  | 
|  366 # The following tests, incrblob2-8.*, test that nothing terrible happens |  | 
|  367 # when a statement transaction is rolled back while there are open  |  | 
|  368 # incremental-blob handles. At one point an assert() was failing when |  | 
|  369 # this was attempted. |  | 
|  370 # |  | 
|  371 do_test incrblob2-8.1 { |  | 
|  372   execsql BEGIN |  | 
|  373   set h [db incrblob t2 B 1] |  | 
|  374   set rc [catch { |  | 
|  375     db eval {SELECT rowid FROM t2} { execsql "DROP TABLE t2" } |  | 
|  376   } msg]  |  | 
|  377   list $rc $msg |  | 
|  378 } {1 {database table is locked}} |  | 
|  379 do_test incrblob2-8.2 { |  | 
|  380   close $h |  | 
|  381   execsql COMMIT |  | 
|  382 } {} |  | 
|  383 do_test incrblob2-8.3 { |  | 
|  384   execsql { |  | 
|  385     CREATE TABLE t3(a INTEGER UNIQUE, b TEXT); |  | 
|  386     INSERT INTO t3 VALUES(1, 'aaaaaaaaaaaaaaaaaaaa'); |  | 
|  387     INSERT INTO t3 VALUES(2, 'bbbbbbbbbbbbbbbbbbbb'); |  | 
|  388     INSERT INTO t3 VALUES(3, 'cccccccccccccccccccc'); |  | 
|  389     INSERT INTO t3 VALUES(4, 'dddddddddddddddddddd'); |  | 
|  390     INSERT INTO t3 VALUES(5, 'eeeeeeeeeeeeeeeeeeee'); |  | 
|  391   } |  | 
|  392 } {} |  | 
|  393 do_test incrblob2-8.4 { |  | 
|  394   execsql BEGIN |  | 
|  395   set h [db incrblob t3 b 3] |  | 
|  396   sqlite3_blob_read $h 0 20 |  | 
|  397 } {cccccccccccccccccccc} |  | 
|  398 do_test incrblob2-8.5 { |  | 
|  399   catchsql {UPDATE t3 SET a = 6 WHERE a > 3} |  | 
|  400 } {1 {column a is not unique}} |  | 
|  401 do_test incrblob2-8.6 { |  | 
|  402   catchsql {UPDATE t3 SET a = 6 WHERE a > 3} |  | 
|  403 } {1 {column a is not unique}} |  | 
|  404 do_test incrblob2-8.7 { |  | 
|  405   sqlite3_blob_read $h 0 20 |  | 
|  406 } {cccccccccccccccccccc} |  | 
|  407 do_test incrblob2-8.8 { |  | 
|  408   catchsql {UPDATE t3 SET a = 6 WHERE a = 3 OR a = 5} |  | 
|  409 } {1 {column a is not unique}} |  | 
|  410 do_test incrblob2-8.9 { |  | 
|  411   set rc [catch {sqlite3_blob_read $h 0 20} msg] |  | 
|  412   list $rc $msg |  | 
|  413 } {1 SQLITE_ABORT} |  | 
|  414 do_test incrblob2-8.X { |  | 
|  415   close $h |  | 
|  416 } {} |  | 
|  417  |  | 
|  418 finish_test |  | 
| OLD | NEW |