gitlab.com/CoiaPrant/sqlite3@v1.19.1/testdata/tcl/fts3cov.test (about)

     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  sqlite3_db_config db DEFENSIVE 0
    93  do_test fts3cov-2.2 {
    94    set root [db one {SELECT root FROM t1_segdir}]
    95    read_fts3varint [string range $root 1 end] left_child
    96    execsql { DELETE FROM t1_segments WHERE blockid = $left_child }
    97  } {}
    98  do_error_test fts3cov-2.3 {
    99    SELECT * FROM t1 WHERE t1 MATCH 'c*'
   100  } {database disk image is malformed}
   101  
   102  # Test the "replaced with NULL" case:
   103  do_test fts3cov-2.4 {
   104    execsql { INSERT INTO t1_segments VALUES($left_child, NULL) }
   105  } {}
   106  do_error_test fts3cov-2.5 {
   107    SELECT * FROM t1 WHERE t1 MATCH 'cloud'
   108  } {database disk image is malformed}
   109  
   110  #--------------------------------------------------------------------------
   111  # The following tests are to test the effects of OOM errors while storing
   112  # terms in the pending-hash table. Specifically, while creating doclist
   113  # blobs to store in the table. More specifically, to test OOM errors while
   114  # appending column numbers to doclists. For example, if a doclist consists
   115  # of:
   116  #
   117  #   <docid> <column 0 offset-list> 0x01 <column N> <column N offset-list>
   118  #
   119  # The following tests check that malloc errors encountered while appending
   120  # the "0x01 <column N>" data to the dynamically growable blob used to 
   121  # accumulate the doclist in memory are handled correctly.
   122  #
   123  do_test fts3cov-3.1 {
   124    set cols [list]
   125    set vals [list]
   126    for {set i 0} {$i < 120} {incr i} {
   127      lappend cols "col$i"
   128      lappend vals "'word'"
   129    }
   130    execsql "CREATE VIRTUAL TABLE t2 USING fts3([join $cols ,])"
   131  } {}
   132  set DO_MALLOC_TEST 1 
   133  do_write_test fts3cov-3.2 t2_content "
   134    INSERT INTO t2(docid, [join $cols ,]) VALUES(1, [join $vals ,])
   135  "
   136  do_write_test fts3cov-3.3 t2_content "
   137    INSERT INTO t2(docid, [join $cols ,]) VALUES(200, [join $vals ,])
   138  "
   139  do_write_test fts3cov-3.4 t2_content "
   140    INSERT INTO t2(docid, [join $cols ,]) VALUES(60000, [join $vals ,])
   141  "
   142  
   143  #-------------------------------------------------------------------------
   144  # If too much data accumulates in the pending-terms hash table, it is
   145  # flushed to the database automatically, even if the transaction has not
   146  # finished. The following tests check the effects of encountering an OOM 
   147  # while doing this.
   148  #
   149  do_test fts3cov-4.1 {
   150    execsql {
   151      CREATE VIRTUAL TABLE t3 USING fts3(x);
   152      INSERT INTO t3(t3) VALUES('nodesize=24');
   153      INSERT INTO t3(t3) VALUES('maxpending=100');
   154    }
   155  } {}
   156  set DO_MALLOC_TEST 1 
   157  do_write_test fts3cov-4.2 t3_content {
   158    INSERT INTO t3(docid, x)
   159      SELECT 1, 'Then Christabel stretched forth her hand,' UNION ALL
   160      SELECT 3, 'And comforted fair Geraldine:'             UNION ALL
   161      SELECT 4, '''O well, bright dame, may you command'    UNION ALL
   162      SELECT 5, 'The service of Sir Leoline;'               UNION ALL
   163      SELECT 2, 'And gladly our stout chivalry'             UNION ALL
   164      SELECT 7, 'Will he send forth, and friends withal,'   UNION ALL
   165      SELECT 8, 'To guide and guard you safe and free'      UNION ALL
   166      SELECT 6, 'Home to your noble father''s hall.'''
   167  }
   168  
   169  #-------------------------------------------------------------------------
   170  # When building the internal tree structure for each segment b-tree, FTS3
   171  # assumes that the content of each internal node will be less than
   172  # $nodesize bytes, where $nodesize is the advisory node size. If this turns
   173  # out to be untrue, then an extra buffer must be malloc'd for each term.
   174  # This test case tests these paths and the effects of said mallocs failing
   175  # by inserting insert a document with some fairly large terms into a
   176  # full-text table with a very small node-size. 
   177  #
   178  # Test this handling of large terms in three contexts:
   179  #
   180  #   1. When flushing the pending-terms table.
   181  #   2. When optimizing the data structures using the INSERT syntax. 
   182  #   2. When optimizing the data structures using the deprecated SELECT syntax. 
   183  #
   184  do_test fts3cov-5.1 {
   185    execsql {
   186      CREATE VIRTUAL TABLE t4 USING fts3(x);
   187      INSERT INTO t4(t4) VALUES('nodesize=24');
   188    }
   189  } {}
   190  set DO_MALLOC_TEST 1
   191  
   192  # Test when flushing pending-terms table.
   193  do_write_test fts3cov-5.2 t4_content {
   194    INSERT INTO t4
   195      SELECT 'ItisanancientMarinerAndhestoppethoneofthreeAA' UNION ALL
   196      SELECT 'ItisanancientMarinerAndhestoppethoneofthreeBB' UNION ALL
   197      SELECT 'ItisanancientMarinerAndhestoppethoneofthreeCC' UNION ALL
   198      SELECT 'BythylonggreybeardandglitteringeyeNowwhereforestoppstAA' UNION ALL
   199      SELECT 'BythylonggreybeardandglitteringeyeNowwhereforestoppstBB' UNION ALL
   200      SELECT 'BythylonggreybeardandglitteringeyeNowwhereforestoppstCC'
   201  }
   202  
   203  # Test when optimizing via INSERT.
   204  do_test fts3cov-5.3 { execsql { INSERT INTO t4 VALUES('extra!') } } {}
   205  do_write_test fts3cov-5.2 t4_segments { INSERT INTO t4(t4) VALUES('optimize') }
   206  
   207  # Test when optimizing via SELECT.
   208  do_test fts3cov-5.5 { execsql { INSERT INTO t4 VALUES('more extra!') } } {}
   209  do_write_test fts3cov-5.6 t4_segments {
   210    SELECT * FROM (SELECT optimize(t4) FROM t4 LIMIT 1)
   211    EXCEPT SELECT 'Index optimized'
   212  }
   213  
   214  #-------------------------------------------------------------------------
   215  # When merging all segments at a given level to create a single segment
   216  # at level+1, FTS3 runs a query of the form:
   217  #
   218  #   SELECT count(*) FROM %_segdir WHERE level = ?
   219  #
   220  # The query is compiled the first time this operation is required and
   221  # reused thereafter. This test aims to test the effects of an OOM while
   222  # preparing and executing this query for the first time.
   223  #
   224  # Then, keep inserting rows into the table so that the effects of an OOM
   225  # while re-executing the same query can also be tested.
   226  #
   227  do_test fts3cov-6.1 {
   228    execsql { CREATE VIRTUAL TABLE t5 USING fts3(x) }
   229    for {set i 0} {$i<16} {incr i} { execsql "INSERT INTO t5 VALUES('term$i')" }
   230    execsql { SELECT count(*) FROM t5_segdir }
   231  } {16}
   232  
   233  # First time.
   234  db close
   235  sqlite3 db test.db
   236  do_write_test fts3cov-6.2 t5_content {
   237    INSERT INTO t5 VALUES('segment number 16!');
   238  }
   239  
   240  # Second time.
   241  do_test fts3cov-6.3 {
   242    for {set i 1} {$i<16} {incr i} { execsql "INSERT INTO t5 VALUES('term$i')" }
   243    execsql { SELECT count(*) FROM t5_segdir }
   244  } {17}
   245  do_write_test fts3cov-6.4 t5_content {
   246    INSERT INTO t5 VALUES('segment number 16!');
   247  }
   248  
   249  #-------------------------------------------------------------------------
   250  # Update the docid of a row. Test this in two scenarios:
   251  #
   252  #   1. When the row being updated is the only row in the table.
   253  #   2. When it is not.
   254  #
   255  # The two cases above take different paths because in case 1 all data 
   256  # structures can simply be emptied before inserting the new row record.
   257  # In case 2, the data structures actually have to be updated.
   258  #
   259  do_test fts3cov-7.1 {
   260    execsql {
   261      CREATE VIRTUAL TABLE t7 USING fts3(a, b, c);
   262      INSERT INTO t7 VALUES('A', 'B', 'C');
   263      UPDATE t7 SET docid = 5;
   264      SELECT docid, * FROM t7;
   265    }
   266  } {5 A B C}
   267  do_test fts3cov-7.2 {
   268    execsql {
   269      INSERT INTO t7 VALUES('D', 'E', 'F');
   270      UPDATE t7 SET docid = 1 WHERE docid = 6;
   271      SELECT docid, * FROM t7;
   272    }
   273  } {1 D E F 5 A B C}
   274  
   275  #-------------------------------------------------------------------------
   276  # If a set of documents are modified within a transaction, the 
   277  # pending-terms table must be flushed each time a document with a docid
   278  # less than or equal to the previous docid is modified. 
   279  #
   280  # This test checks the effects of an OOM error occuring when the 
   281  # pending-terms table is flushed for this reason as part of a DELETE 
   282  # statement.
   283  #
   284  do_malloc_test fts3cov-8 -sqlprep {
   285    BEGIN;
   286      CREATE VIRTUAL TABLE t8 USING fts3;
   287      INSERT INTO t8 VALUES('the output of each batch run');
   288      INSERT INTO t8 VALUES('(possibly a day''s work)');
   289      INSERT INTO t8 VALUES('was written to two separate disks');
   290    COMMIT;
   291  } -sqlbody {
   292    BEGIN;
   293      DELETE FROM t8 WHERE rowid = 3;
   294      DELETE FROM t8 WHERE rowid = 2;
   295      DELETE FROM t8 WHERE rowid = 1;
   296    COMMIT;
   297  }
   298  
   299  #-------------------------------------------------------------------------
   300  # Test some branches in the code that handles "special" inserts like:
   301  #
   302  #   INSERT INTO t1(t1) VALUES('optimize');
   303  #
   304  # Also test that an optimize (INSERT method) works on an empty table.
   305  #
   306  set DO_MALLOC_TEST 0
   307  do_test fts3cov-9.1 {
   308    execsql { CREATE VIRTUAL TABLE xx USING fts3 }
   309  } {}
   310  do_error_test fts3cov-9.2 {
   311    INSERT INTO xx(xx) VALUES('optimise');   -- British spelling
   312  } {SQL logic error}
   313  do_error_test fts3cov-9.3 {
   314    INSERT INTO xx(xx) VALUES('short');
   315  } {SQL logic error}
   316  do_error_test fts3cov-9.4 {
   317    INSERT INTO xx(xx) VALUES('waytoolongtobecorrect');
   318  } {SQL logic error}
   319  do_test fts3cov-9.5 {
   320    execsql { INSERT INTO xx(xx) VALUES('optimize') }
   321  } {}
   322  
   323  #-------------------------------------------------------------------------
   324  # Test that a table can be optimized in the middle of a transaction when
   325  # the pending-terms table is non-empty. This case involves some extra
   326  # branches because data must be read not only from the database, but
   327  # also from the pending-terms table.
   328  #
   329  do_malloc_test fts3cov-10 -sqlprep {
   330    CREATE VIRTUAL TABLE t10 USING fts3;
   331    INSERT INTO t10 VALUES('Optimising images for the web is a tricky business');
   332    BEGIN;
   333      INSERT INTO t10 VALUES('You have to get the right balance between');
   334  } -sqlbody {
   335    INSERT INTO t10(t10) VALUES('optimize');
   336  }
   337  
   338  #-------------------------------------------------------------------------
   339  # Test a full-text query for a term that was once in the index, but is
   340  # no longer.
   341  #
   342  do_test fts3cov-11.1 {
   343    execsql { 
   344      CREATE VIRTUAL TABLE xx USING fts3;
   345      INSERT INTO xx VALUES('one two three');
   346      INSERT INTO xx VALUES('four five six');
   347      DELETE FROM xx WHERE docid = 1;
   348    }
   349    execsql { SELECT * FROM xx WHERE xx MATCH 'two' }
   350  } {}
   351  
   352  
   353  do_malloc_test fts3cov-12 -sqlprep {
   354    CREATE VIRTUAL TABLE t12 USING fts3;
   355    INSERT INTO t12 VALUES('is one of the two togther');
   356    BEGIN;
   357      INSERT INTO t12 VALUES('one which was appropriate at the time');
   358  } -sqlbody {
   359    SELECT * FROM t12 WHERE t12 MATCH 'one'
   360  }
   361  
   362  do_malloc_test fts3cov-13 -sqlprep {
   363    PRAGMA encoding = 'UTF-16';
   364    CREATE VIRTUAL TABLE t13 USING fts3;
   365    INSERT INTO t13 VALUES('two scalar functions');
   366    INSERT INTO t13 VALUES('scalar two functions');
   367    INSERT INTO t13 VALUES('functions scalar two');
   368  } -sqlbody {
   369    SELECT snippet(t13, '%%', '%%', '#') FROM t13 WHERE t13 MATCH 'two';
   370    SELECT snippet(t13, '%%', '%%') FROM t13 WHERE t13 MATCH 'two';
   371    SELECT snippet(t13, '%%') FROM t13 WHERE t13 MATCH 'two';
   372  }
   373  
   374  do_execsql_test 14.0 {
   375    CREATE VIRTUAL TABLE t14 USING fts4(a, b);
   376    INSERT INTO t14 VALUES('one two three', 'one three four');
   377    INSERT INTO t14 VALUES('a b c', 'd e a');
   378  }
   379  do_execsql_test 14.1 {
   380    SELECT rowid FROM t14 WHERE t14 MATCH '"one two three"'
   381  } {1}
   382  do_execsql_test 14.2 {
   383    SELECT rowid FROM t14 WHERE t14 MATCH '"one four"'
   384  } {}
   385  do_execsql_test 14.3 {
   386    SELECT rowid FROM t14 WHERE t14 MATCH '"e a"'
   387  } {2}
   388  do_execsql_test 14.5 {
   389    SELECT rowid FROM t14 WHERE t14 MATCH '"e b"'
   390  } {}
   391  do_catchsql_test 14.6 {
   392    SELECT rowid FROM t14 WHERE rowid MATCH 'one'
   393  } {1 {unable to use function MATCH in the requested context}}
   394  do_catchsql_test 14.7 {
   395    SELECT rowid FROM t14 WHERE docid MATCH 'one'
   396  } {1 {unable to use function MATCH in the requested context}}
   397  
   398  do_execsql_test 15.0 {
   399    CREATE VIRTUAL TABLE t15 USING fts4(a, b, c);
   400    INSERT INTO t15 VALUES('abc def ghi', 'abc2 def2 ghi2', 'abc3 def3 ghi3');
   401    INSERT INTO t15 VALUES('abc2 def2 ghi2', 'abc2 def2 ghi2', 'abc def3 ghi3');
   402  }
   403  do_execsql_test 15.1 {
   404    SELECT rowid FROM t15 WHERE t15 MATCH '"abc* def2"'
   405  } {1 2}
   406  
   407  # Test a corruption case.
   408  #
   409  sqlite3_db_config db DEFENSIVE 0
   410  do_execsql_test 16.1 {
   411    CREATE VIRTUAL TABLE t16 USING fts4;
   412    INSERT INTO t16 VALUES('theoretical work to examine the relationship');
   413    INSERT INTO t16 VALUES('solution of our problems on the invisible');
   414    DELETE FROM t16_content WHERE rowid = 2;
   415  }
   416  do_catchsql_test 16.2 {
   417    SELECT * FROM t16 WHERE t16 MATCH 'invisible'
   418  } {1 {database disk image is malformed}}
   419  
   420  # And another corruption test case.
   421  #
   422  do_execsql_test 17.1 {
   423    CREATE VIRTUAL TABLE t17 USING fts4;
   424    INSERT INTO t17(content) VALUES('one one one');
   425    UPDATE t17_segdir SET root = X'00036F6E65FFFFFFFFFFFFFFFFFFFFFF02030300'
   426  } {}
   427  do_catchsql_test 17.2 {
   428    SELECT * FROM t17 WHERE t17 MATCH 'one'
   429  } {1 {database disk image is malformed}}
   430  
   431  
   432  
   433  
   434  finish_test