Annotation of embedaddon/sqlite3/test/fts3cov.test, revision 1.1
1.1 ! misho 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
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>