OLD | NEW |
(Empty) | |
| 1 # 2009 December 03 |
| 2 # |
| 3 # May you do good and not evil. |
| 4 # May you find forgiveness for yourself and forgive others. |
| 5 # May you share freely, never taking more than you give. |
| 6 # |
| 7 #*********************************************************************** |
| 8 # |
| 9 # The tests in this file are structural coverage tests for FTS3. |
| 10 # |
| 11 |
| 12 set testdir [file dirname $argv0] |
| 13 source $testdir/tester.tcl |
| 14 |
| 15 # If this build does not include FTS3, skip the tests in this file. |
| 16 # |
| 17 ifcapable !fts3 { finish_test ; return } |
| 18 source $testdir/fts3_common.tcl |
| 19 source $testdir/malloc_common.tcl |
| 20 |
| 21 set DO_MALLOC_TEST 0 |
| 22 set testprefix fts3cov |
| 23 |
| 24 #-------------------------------------------------------------------------- |
| 25 # When it first needs to read a block from the %_segments table, the FTS3 |
| 26 # module compiles an SQL statement for that purpose. The statement is |
| 27 # stored and reused each subsequent time a block is read. This test case |
| 28 # tests the effects of an OOM error occuring while compiling the statement. |
| 29 # |
| 30 # Similarly, when FTS3 first needs to scan through a set of segment leaves |
| 31 # to find a set of documents that matches a term, it allocates a string |
| 32 # containing the text of the required SQL, and compiles one or more |
| 33 # statements to traverse the leaves. This test case tests that OOM errors |
| 34 # that occur while allocating this string and statement are handled correctly |
| 35 # also. |
| 36 # |
| 37 do_test fts3cov-1.1 { |
| 38 execsql { |
| 39 CREATE VIRTUAL TABLE t1 USING fts3(x); |
| 40 INSERT INTO t1(t1) VALUES('nodesize=24'); |
| 41 BEGIN; |
| 42 INSERT INTO t1 VALUES('Is the night chilly and dark?'); |
| 43 INSERT INTO t1 VALUES('The night is chilly, but not dark.'); |
| 44 INSERT INTO t1 VALUES('The thin gray cloud is spread on high,'); |
| 45 INSERT INTO t1 VALUES('It covers but not hides the sky.'); |
| 46 COMMIT; |
| 47 SELECT count(*)>0 FROM t1_segments; |
| 48 } |
| 49 } {1} |
| 50 |
| 51 set DO_MALLOC_TEST 1 |
| 52 do_restart_select_test fts3cov-1.2 { |
| 53 SELECT docid FROM t1 WHERE t1 MATCH 'chilly'; |
| 54 } {1 2} |
| 55 set DO_MALLOC_TEST 0 |
| 56 |
| 57 #-------------------------------------------------------------------------- |
| 58 # When querying the full-text index, if an expected internal node block is |
| 59 # missing from the %_segments table, or if a NULL value is stored in the |
| 60 # %_segments table instead of a binary blob, database corruption should be |
| 61 # reported. |
| 62 # |
| 63 # Even with tiny 24 byte nodes, it takes a fair bit of data to produce a |
| 64 # segment b-tree that uses the %_segments table to store internal nodes. |
| 65 # |
| 66 do_test fts3cov-2.1 { |
| 67 execsql { |
| 68 INSERT INTO t1(t1) VALUES('nodesize=24'); |
| 69 BEGIN; |
| 70 INSERT INTO t1 VALUES('The moon is behind, and at the full;'); |
| 71 INSERT INTO t1 VALUES('And yet she looks both small and dull.'); |
| 72 INSERT INTO t1 VALUES('The night is chill, the cloud is gray:'); |
| 73 INSERT INTO t1 VALUES('''T is a month before the month of May,'); |
| 74 INSERT INTO t1 VALUES('And the Spring comes slowly up this way.'); |
| 75 INSERT INTO t1 VALUES('The lovely lady, Christabel,'); |
| 76 INSERT INTO t1 VALUES('Whom her father loves so well,'); |
| 77 INSERT INTO t1 VALUES('What makes her in the wood so late,'); |
| 78 INSERT INTO t1 VALUES('A furlong from the castle gate?'); |
| 79 INSERT INTO t1 VALUES('She had dreams all yesternight'); |
| 80 INSERT INTO t1 VALUES('Of her own betrothed knight;'); |
| 81 INSERT INTO t1 VALUES('And she in the midnight wood will pray'); |
| 82 INSERT INTO t1 VALUES('For the weal of her lover that''s far away.'); |
| 83 COMMIT; |
| 84 } |
| 85 execsql { |
| 86 INSERT INTO t1(t1) VALUES('optimize'); |
| 87 SELECT substr(hex(root), 1, 2) FROM t1_segdir; |
| 88 } |
| 89 } {03} |
| 90 |
| 91 # Test the "missing entry" case: |
| 92 do_test fts3cov-2.2 { |
| 93 set root [db one {SELECT root FROM t1_segdir}] |
| 94 read_fts3varint [string range $root 1 end] left_child |
| 95 execsql { DELETE FROM t1_segments WHERE blockid = $left_child } |
| 96 } {} |
| 97 do_error_test fts3cov-2.3 { |
| 98 SELECT * FROM t1 WHERE t1 MATCH 'c*' |
| 99 } {SQL logic error or missing database} |
| 100 |
| 101 # Test the "replaced with NULL" case: |
| 102 do_test fts3cov-2.4 { |
| 103 execsql { INSERT INTO t1_segments VALUES($left_child, NULL) } |
| 104 } {} |
| 105 do_error_test fts3cov-2.5 { |
| 106 SELECT * FROM t1 WHERE t1 MATCH 'cloud' |
| 107 } {SQL logic error or missing database} |
| 108 |
| 109 #-------------------------------------------------------------------------- |
| 110 # The following tests are to test the effects of OOM errors while storing |
| 111 # terms in the pending-hash table. Specifically, while creating doclist |
| 112 # blobs to store in the table. More specifically, to test OOM errors while |
| 113 # appending column numbers to doclists. For example, if a doclist consists |
| 114 # of: |
| 115 # |
| 116 # <docid> <column 0 offset-list> 0x01 <column N> <column N offset-list> |
| 117 # |
| 118 # The following tests check that malloc errors encountered while appending |
| 119 # the "0x01 <column N>" data to the dynamically growable blob used to |
| 120 # accumulate the doclist in memory are handled correctly. |
| 121 # |
| 122 do_test fts3cov-3.1 { |
| 123 set cols [list] |
| 124 set vals [list] |
| 125 for {set i 0} {$i < 120} {incr i} { |
| 126 lappend cols "col$i" |
| 127 lappend vals "'word'" |
| 128 } |
| 129 execsql "CREATE VIRTUAL TABLE t2 USING fts3([join $cols ,])" |
| 130 } {} |
| 131 set DO_MALLOC_TEST 1 |
| 132 do_write_test fts3cov-3.2 t2_content " |
| 133 INSERT INTO t2(docid, [join $cols ,]) VALUES(1, [join $vals ,]) |
| 134 " |
| 135 do_write_test fts3cov-3.3 t2_content " |
| 136 INSERT INTO t2(docid, [join $cols ,]) VALUES(200, [join $vals ,]) |
| 137 " |
| 138 do_write_test fts3cov-3.4 t2_content " |
| 139 INSERT INTO t2(docid, [join $cols ,]) VALUES(60000, [join $vals ,]) |
| 140 " |
| 141 |
| 142 #------------------------------------------------------------------------- |
| 143 # If too much data accumulates in the pending-terms hash table, it is |
| 144 # flushed to the database automatically, even if the transaction has not |
| 145 # finished. The following tests check the effects of encountering an OOM |
| 146 # while doing this. |
| 147 # |
| 148 do_test fts3cov-4.1 { |
| 149 execsql { |
| 150 CREATE VIRTUAL TABLE t3 USING fts3(x); |
| 151 INSERT INTO t3(t3) VALUES('nodesize=24'); |
| 152 INSERT INTO t3(t3) VALUES('maxpending=100'); |
| 153 } |
| 154 } {} |
| 155 set DO_MALLOC_TEST 1 |
| 156 do_write_test fts3cov-4.2 t3_content { |
| 157 INSERT INTO t3(docid, x) |
| 158 SELECT 1, 'Then Christabel stretched forth her hand,' UNION ALL |
| 159 SELECT 3, 'And comforted fair Geraldine:' UNION ALL |
| 160 SELECT 4, '''O well, bright dame, may you command' UNION ALL |
| 161 SELECT 5, 'The service of Sir Leoline;' UNION ALL |
| 162 SELECT 2, 'And gladly our stout chivalry' UNION ALL |
| 163 SELECT 7, 'Will he send forth, and friends withal,' UNION ALL |
| 164 SELECT 8, 'To guide and guard you safe and free' UNION ALL |
| 165 SELECT 6, 'Home to your noble father''s hall.''' |
| 166 } |
| 167 |
| 168 #------------------------------------------------------------------------- |
| 169 # When building the internal tree structure for each segment b-tree, FTS3 |
| 170 # assumes that the content of each internal node will be less than |
| 171 # $nodesize bytes, where $nodesize is the advisory node size. If this turns |
| 172 # out to be untrue, then an extra buffer must be malloc'd for each term. |
| 173 # This test case tests these paths and the effects of said mallocs failing |
| 174 # by inserting insert a document with some fairly large terms into a |
| 175 # full-text table with a very small node-size. |
| 176 # |
| 177 # Test this handling of large terms in three contexts: |
| 178 # |
| 179 # 1. When flushing the pending-terms table. |
| 180 # 2. When optimizing the data structures using the INSERT syntax. |
| 181 # 2. When optimizing the data structures using the deprecated SELECT syntax. |
| 182 # |
| 183 do_test fts3cov-5.1 { |
| 184 execsql { |
| 185 CREATE VIRTUAL TABLE t4 USING fts3(x); |
| 186 INSERT INTO t4(t4) VALUES('nodesize=24'); |
| 187 } |
| 188 } {} |
| 189 set DO_MALLOC_TEST 1 |
| 190 |
| 191 # Test when flushing pending-terms table. |
| 192 do_write_test fts3cov-5.2 t4_content { |
| 193 INSERT INTO t4 |
| 194 SELECT 'ItisanancientMarinerAndhestoppethoneofthreeAA' UNION ALL |
| 195 SELECT 'ItisanancientMarinerAndhestoppethoneofthreeBB' UNION ALL |
| 196 SELECT 'ItisanancientMarinerAndhestoppethoneofthreeCC' UNION ALL |
| 197 SELECT 'BythylonggreybeardandglitteringeyeNowwhereforestoppstAA' UNION ALL |
| 198 SELECT 'BythylonggreybeardandglitteringeyeNowwhereforestoppstBB' UNION ALL |
| 199 SELECT 'BythylonggreybeardandglitteringeyeNowwhereforestoppstCC' |
| 200 } |
| 201 |
| 202 # Test when optimizing via INSERT. |
| 203 do_test fts3cov-5.3 { execsql { INSERT INTO t4 VALUES('extra!') } } {} |
| 204 do_write_test fts3cov-5.2 t4_segments { INSERT INTO t4(t4) VALUES('optimize') } |
| 205 |
| 206 # Test when optimizing via SELECT. |
| 207 do_test fts3cov-5.5 { execsql { INSERT INTO t4 VALUES('more extra!') } } {} |
| 208 do_write_test fts3cov-5.6 t4_segments { |
| 209 SELECT * FROM (SELECT optimize(t4) FROM t4 LIMIT 1) |
| 210 EXCEPT SELECT 'Index optimized' |
| 211 } |
| 212 |
| 213 #------------------------------------------------------------------------- |
| 214 # When merging all segments at a given level to create a single segment |
| 215 # at level+1, FTS3 runs a query of the form: |
| 216 # |
| 217 # SELECT count(*) FROM %_segdir WHERE level = ? |
| 218 # |
| 219 # The query is compiled the first time this operation is required and |
| 220 # reused thereafter. This test aims to test the effects of an OOM while |
| 221 # preparing and executing this query for the first time. |
| 222 # |
| 223 # Then, keep inserting rows into the table so that the effects of an OOM |
| 224 # while re-executing the same query can also be tested. |
| 225 # |
| 226 do_test fts3cov-6.1 { |
| 227 execsql { CREATE VIRTUAL TABLE t5 USING fts3(x) } |
| 228 for {set i 0} {$i<16} {incr i} { execsql "INSERT INTO t5 VALUES('term$i')" } |
| 229 execsql { SELECT count(*) FROM t5_segdir } |
| 230 } {16} |
| 231 |
| 232 # First time. |
| 233 db close |
| 234 sqlite3 db test.db |
| 235 do_write_test fts3cov-6.2 t5_content { |
| 236 INSERT INTO t5 VALUES('segment number 16!'); |
| 237 } |
| 238 |
| 239 # Second time. |
| 240 do_test fts3cov-6.3 { |
| 241 for {set i 1} {$i<16} {incr i} { execsql "INSERT INTO t5 VALUES('term$i')" } |
| 242 execsql { SELECT count(*) FROM t5_segdir } |
| 243 } {17} |
| 244 do_write_test fts3cov-6.4 t5_content { |
| 245 INSERT INTO t5 VALUES('segment number 16!'); |
| 246 } |
| 247 |
| 248 #------------------------------------------------------------------------- |
| 249 # Update the docid of a row. Test this in two scenarios: |
| 250 # |
| 251 # 1. When the row being updated is the only row in the table. |
| 252 # 2. When it is not. |
| 253 # |
| 254 # The two cases above take different paths because in case 1 all data |
| 255 # structures can simply be emptied before inserting the new row record. |
| 256 # In case 2, the data structures actually have to be updated. |
| 257 # |
| 258 do_test fts3cov-7.1 { |
| 259 execsql { |
| 260 CREATE VIRTUAL TABLE t7 USING fts3(a, b, c); |
| 261 INSERT INTO t7 VALUES('A', 'B', 'C'); |
| 262 UPDATE t7 SET docid = 5; |
| 263 SELECT docid, * FROM t7; |
| 264 } |
| 265 } {5 A B C} |
| 266 do_test fts3cov-7.2 { |
| 267 execsql { |
| 268 INSERT INTO t7 VALUES('D', 'E', 'F'); |
| 269 UPDATE t7 SET docid = 1 WHERE docid = 6; |
| 270 SELECT docid, * FROM t7; |
| 271 } |
| 272 } {1 D E F 5 A B C} |
| 273 |
| 274 #------------------------------------------------------------------------- |
| 275 # If a set of documents are modified within a transaction, the |
| 276 # pending-terms table must be flushed each time a document with a docid |
| 277 # less than or equal to the previous docid is modified. |
| 278 # |
| 279 # This test checks the effects of an OOM error occuring when the |
| 280 # pending-terms table is flushed for this reason as part of a DELETE |
| 281 # statement. |
| 282 # |
| 283 do_malloc_test fts3cov-8 -sqlprep { |
| 284 BEGIN; |
| 285 CREATE VIRTUAL TABLE t8 USING fts3; |
| 286 INSERT INTO t8 VALUES('the output of each batch run'); |
| 287 INSERT INTO t8 VALUES('(possibly a day''s work)'); |
| 288 INSERT INTO t8 VALUES('was written to two separate disks'); |
| 289 COMMIT; |
| 290 } -sqlbody { |
| 291 BEGIN; |
| 292 DELETE FROM t8 WHERE rowid = 3; |
| 293 DELETE FROM t8 WHERE rowid = 2; |
| 294 DELETE FROM t8 WHERE rowid = 1; |
| 295 COMMIT; |
| 296 } |
| 297 |
| 298 #------------------------------------------------------------------------- |
| 299 # Test some branches in the code that handles "special" inserts like: |
| 300 # |
| 301 # INSERT INTO t1(t1) VALUES('optimize'); |
| 302 # |
| 303 # Also test that an optimize (INSERT method) works on an empty table. |
| 304 # |
| 305 set DO_MALLOC_TEST 0 |
| 306 do_test fts3cov-9.1 { |
| 307 execsql { CREATE VIRTUAL TABLE xx USING fts3 } |
| 308 } {} |
| 309 do_error_test fts3cov-9.2 { |
| 310 INSERT INTO xx(xx) VALUES('optimise'); -- British spelling |
| 311 } {SQL logic error or missing database} |
| 312 do_error_test fts3cov-9.3 { |
| 313 INSERT INTO xx(xx) VALUES('short'); |
| 314 } {SQL logic error or missing database} |
| 315 do_error_test fts3cov-9.4 { |
| 316 INSERT INTO xx(xx) VALUES('waytoolongtobecorrect'); |
| 317 } {SQL logic error or missing database} |
| 318 do_test fts3cov-9.5 { |
| 319 execsql { INSERT INTO xx(xx) VALUES('optimize') } |
| 320 } {} |
| 321 |
| 322 #------------------------------------------------------------------------- |
| 323 # Test that a table can be optimized in the middle of a transaction when |
| 324 # the pending-terms table is non-empty. This case involves some extra |
| 325 # branches because data must be read not only from the database, but |
| 326 # also from the pending-terms table. |
| 327 # |
| 328 do_malloc_test fts3cov-10 -sqlprep { |
| 329 CREATE VIRTUAL TABLE t10 USING fts3; |
| 330 INSERT INTO t10 VALUES('Optimising images for the web is a tricky business'); |
| 331 BEGIN; |
| 332 INSERT INTO t10 VALUES('You have to get the right balance between'); |
| 333 } -sqlbody { |
| 334 INSERT INTO t10(t10) VALUES('optimize'); |
| 335 } |
| 336 |
| 337 #------------------------------------------------------------------------- |
| 338 # Test a full-text query for a term that was once in the index, but is |
| 339 # no longer. |
| 340 # |
| 341 do_test fts3cov-11.1 { |
| 342 execsql { |
| 343 CREATE VIRTUAL TABLE xx USING fts3; |
| 344 INSERT INTO xx VALUES('one two three'); |
| 345 INSERT INTO xx VALUES('four five six'); |
| 346 DELETE FROM xx WHERE docid = 1; |
| 347 } |
| 348 execsql { SELECT * FROM xx WHERE xx MATCH 'two' } |
| 349 } {} |
| 350 |
| 351 |
| 352 do_malloc_test fts3cov-12 -sqlprep { |
| 353 CREATE VIRTUAL TABLE t12 USING fts3; |
| 354 INSERT INTO t12 VALUES('is one of the two togther'); |
| 355 BEGIN; |
| 356 INSERT INTO t12 VALUES('one which was appropriate at the time'); |
| 357 } -sqlbody { |
| 358 SELECT * FROM t12 WHERE t12 MATCH 'one' |
| 359 } |
| 360 |
| 361 do_malloc_test fts3cov-13 -sqlprep { |
| 362 PRAGMA encoding = 'UTF-16'; |
| 363 CREATE VIRTUAL TABLE t13 USING fts3; |
| 364 INSERT INTO t13 VALUES('two scalar functions'); |
| 365 INSERT INTO t13 VALUES('scalar two functions'); |
| 366 INSERT INTO t13 VALUES('functions scalar two'); |
| 367 } -sqlbody { |
| 368 SELECT snippet(t13, '%%', '%%', '#') FROM t13 WHERE t13 MATCH 'two'; |
| 369 SELECT snippet(t13, '%%', '%%') FROM t13 WHERE t13 MATCH 'two'; |
| 370 SELECT snippet(t13, '%%') FROM t13 WHERE t13 MATCH 'two'; |
| 371 } |
| 372 |
| 373 do_execsql_test 14.0 { |
| 374 CREATE VIRTUAL TABLE t14 USING fts4(a, b); |
| 375 INSERT INTO t14 VALUES('one two three', 'one three four'); |
| 376 INSERT INTO t14 VALUES('a b c', 'd e a'); |
| 377 } |
| 378 do_execsql_test 14.1 { |
| 379 SELECT rowid FROM t14 WHERE t14 MATCH '"one two three"' |
| 380 } {1} |
| 381 do_execsql_test 14.2 { |
| 382 SELECT rowid FROM t14 WHERE t14 MATCH '"one four"' |
| 383 } {} |
| 384 do_execsql_test 14.3 { |
| 385 SELECT rowid FROM t14 WHERE t14 MATCH '"e a"' |
| 386 } {2} |
| 387 do_execsql_test 14.5 { |
| 388 SELECT rowid FROM t14 WHERE t14 MATCH '"e b"' |
| 389 } {} |
| 390 do_catchsql_test 14.6 { |
| 391 SELECT rowid FROM t14 WHERE rowid MATCH 'one' |
| 392 } {1 {unable to use function MATCH in the requested context}} |
| 393 do_catchsql_test 14.7 { |
| 394 SELECT rowid FROM t14 WHERE docid MATCH 'one' |
| 395 } {1 {unable to use function MATCH in the requested context}} |
| 396 |
| 397 do_execsql_test 15.0 { |
| 398 CREATE VIRTUAL TABLE t15 USING fts4(a, b, c); |
| 399 INSERT INTO t15 VALUES('abc def ghi', 'abc2 def2 ghi2', 'abc3 def3 ghi3'); |
| 400 INSERT INTO t15 VALUES('abc2 def2 ghi2', 'abc2 def2 ghi2', 'abc def3 ghi3'); |
| 401 } |
| 402 do_execsql_test 15.1 { |
| 403 SELECT rowid FROM t15 WHERE t15 MATCH '"abc* def2"' |
| 404 } {1 2} |
| 405 |
| 406 # Test a corruption case. |
| 407 # |
| 408 do_execsql_test 16.1 { |
| 409 CREATE VIRTUAL TABLE t16 USING fts4; |
| 410 INSERT INTO t16 VALUES('theoretical work to examine the relationship'); |
| 411 INSERT INTO t16 VALUES('solution of our problems on the invisible'); |
| 412 DELETE FROM t16_content WHERE rowid = 2; |
| 413 } |
| 414 do_catchsql_test 16.2 { |
| 415 SELECT * FROM t16 WHERE t16 MATCH 'invisible' |
| 416 } {1 {database disk image is malformed}} |
| 417 |
| 418 # And another corruption test case. |
| 419 # |
| 420 do_execsql_test 17.1 { |
| 421 CREATE VIRTUAL TABLE t17 USING fts4; |
| 422 INSERT INTO t17(content) VALUES('one one one'); |
| 423 UPDATE t17_segdir SET root = X'00036F6E65FFFFFFFFFFFFFFFFFFFFFF02030300' |
| 424 } {} |
| 425 do_catchsql_test 17.2 { |
| 426 SELECT * FROM t17 WHERE t17 MATCH 'one' |
| 427 } {1 {database disk image is malformed}} |
| 428 |
| 429 |
| 430 |
| 431 |
| 432 finish_test |
OLD | NEW |