/ Check-in [918b6092]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Further improvements to coverage of fts3.c. Fixes for bugs revealed by the same.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | experimental
Files: files | file ages | folders
SHA1:918b609290127f54326c638d82837d117398eade
User & Date: dan 2010-10-25 19:01:25
Context
2010-10-26
07:14
More coverage tests for fts3.c. check-in: 7a2f2864 user: dan tags: experimental
2010-10-25
19:01
Further improvements to coverage of fts3.c. Fixes for bugs revealed by the same. check-in: 918b6092 user: dan tags: experimental
12:47
Test coverage improvements for fts3.c. check-in: a8b1d998 user: dan tags: experimental
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to ext/fts3/fts3.c.

   556    556   static int fts3CreateTables(Fts3Table *p){
   557    557     int rc = SQLITE_OK;             /* Return code */
   558    558     int i;                          /* Iterator variable */
   559    559     char *zContentCols;             /* Columns of %_content table */
   560    560     sqlite3 *db = p->db;            /* The database connection */
   561    561   
   562    562     /* Create a list of user columns for the content table */
   563         -  if( p->bHasContent ){
   564         -    zContentCols = sqlite3_mprintf("docid INTEGER PRIMARY KEY");
   565         -    for(i=0; zContentCols && i<p->nColumn; i++){
   566         -      char *z = p->azColumn[i];
   567         -      zContentCols = sqlite3_mprintf("%z, 'c%d%q'", zContentCols, i, z);
   568         -    }
   569         -    if( zContentCols==0 ) rc = SQLITE_NOMEM;
          563  +  zContentCols = sqlite3_mprintf("docid INTEGER PRIMARY KEY");
          564  +  for(i=0; zContentCols && i<p->nColumn; i++){
          565  +    char *z = p->azColumn[i];
          566  +    zContentCols = sqlite3_mprintf("%z, 'c%d%q'", zContentCols, i, z);
          567  +  }
          568  +  if( zContentCols==0 ) rc = SQLITE_NOMEM;
   570    569   
   571         -    /* Create the content table */
   572         -    fts3DbExec(&rc, db, 
   573         -       "CREATE TABLE %Q.'%q_content'(%s)",
   574         -       p->zDb, p->zName, zContentCols
   575         -    );
   576         -    sqlite3_free(zContentCols);
   577         -  }
          570  +  /* Create the content table */
          571  +  fts3DbExec(&rc, db, 
          572  +     "CREATE TABLE %Q.'%q_content'(%s)",
          573  +     p->zDb, p->zName, zContentCols
          574  +  );
          575  +  sqlite3_free(zContentCols);
   578    576     /* Create other tables */
   579    577     fts3DbExec(&rc, db, 
   580    578         "CREATE TABLE %Q.'%q_segments'(blockid INTEGER PRIMARY KEY, block BLOB);",
   581    579         p->zDb, p->zName
   582    580     );
   583    581     fts3DbExec(&rc, db, 
   584    582         "CREATE TABLE %Q.'%q_segdir'("
................................................................................
  1099   1097     int rc;                         /* Return code */
  1100   1098     int iHeight;                    /* Height of this node in tree */
  1101   1099   
  1102   1100     assert( piLeaf || piLeaf2 );
  1103   1101   
  1104   1102     sqlite3Fts3GetVarint32(zNode, &iHeight);
  1105   1103     rc = fts3ScanInteriorNode(p, zTerm, nTerm, zNode, nNode, piLeaf, piLeaf2);
  1106         -  
         1104  +  assert( !piLeaf2 || !piLeaf || rc!=SQLITE_OK || (*piLeaf<=*piLeaf2) );
         1105  +
  1107   1106     if( rc==SQLITE_OK && iHeight>1 ){
  1108   1107       char *zBlob = 0;              /* Blob read from %_segments table */
  1109   1108       int nBlob;                    /* Size of zBlob in bytes */
  1110   1109   
  1111   1110       if( piLeaf && piLeaf2 && (*piLeaf!=*piLeaf2) ){
  1112   1111         rc = sqlite3Fts3ReadBlock(p, *piLeaf, &zBlob, &nBlob);
  1113   1112         if( rc==SQLITE_OK ){
................................................................................
  1114   1113           rc = fts3SelectLeaf(p, zTerm, nTerm, zBlob, nBlob, piLeaf, 0);
  1115   1114         }
  1116   1115         sqlite3_free(zBlob);
  1117   1116         piLeaf = 0;
  1118   1117         zBlob = 0;
  1119   1118       }
  1120   1119   
  1121         -    rc = sqlite3Fts3ReadBlock(p, piLeaf ? *piLeaf : *piLeaf2, &zBlob, &nBlob);
         1120  +    if( rc==SQLITE_OK ){
         1121  +      rc = sqlite3Fts3ReadBlock(p, piLeaf ? *piLeaf : *piLeaf2, &zBlob, &nBlob);
         1122  +    }
  1122   1123       if( rc==SQLITE_OK ){
  1123   1124         rc = fts3SelectLeaf(p, zTerm, nTerm, zBlob, nBlob, piLeaf, piLeaf2);
  1124   1125       }
  1125   1126       sqlite3_free(zBlob);
  1126   1127     }
  1127   1128   
  1128   1129     return rc;
................................................................................
  1746   1747     ** into a single doclist.
  1747   1748     */
  1748   1749     for(i=0; i<SizeofArray(pTS->aaOutput); i++){
  1749   1750       if( pTS->aaOutput[i] ){
  1750   1751         if( !aOut ){
  1751   1752           aOut = pTS->aaOutput[i];
  1752   1753           nOut = pTS->anOutput[i];
  1753         -        pTS->aaOutput[0] = 0;
         1754  +        pTS->aaOutput[i] = 0;
  1754   1755         }else{
  1755   1756           int nNew = nOut + pTS->anOutput[i];
  1756   1757           char *aNew = sqlite3_malloc(nNew);
  1757   1758           if( !aNew ){
  1758   1759             sqlite3_free(aOut);
  1759   1760             return SQLITE_NOMEM;
  1760   1761           }
................................................................................
  2250   2251       if( isFirst ){
  2251   2252         pOut = pList;
  2252   2253         nOut = nList;
  2253   2254         if( pCsr->eEvalmode==FTS3_EVAL_FILTER && pPhrase->nToken>1 ){
  2254   2255           nDoc = fts3DoclistCountDocids(1, pOut, nOut);
  2255   2256         }
  2256   2257         isFirst = 0;
         2258  +      iPrevTok = iTok;
  2257   2259       }else{
  2258   2260         /* Merge the new term list and the current output. */
  2259   2261         char *aLeft, *aRight;
  2260   2262         int nLeft, nRight;
  2261   2263         int nDist;
  2262   2264         int mt;
  2263   2265   
................................................................................
  2271   2273         assert( iPrevTok!=iTok );
  2272   2274         if( iPrevTok<iTok ){
  2273   2275           aLeft = pOut;
  2274   2276           nLeft = nOut;
  2275   2277           aRight = pList;
  2276   2278           nRight = nList;
  2277   2279           nDist = iTok-iPrevTok;
         2280  +        iPrevTok = iTok;
  2278   2281         }else{
  2279   2282           aRight = pOut;
  2280   2283           nRight = nOut;
  2281   2284           aLeft = pList;
  2282   2285           nLeft = nList;
  2283   2286           nDist = iPrevTok-iTok;
  2284   2287         }
................................................................................
  2285   2288         pOut = aRight;
  2286   2289         fts3DoclistMerge(
  2287   2290             mt, nDist, 0, pOut, &nOut, aLeft, nLeft, aRight, nRight, &nDoc
  2288   2291         );
  2289   2292         sqlite3_free(aLeft);
  2290   2293       }
  2291   2294       assert( nOut==0 || pOut!=0 );
  2292         -
  2293         -    iPrevTok = iTok;
  2294   2295     }
  2295   2296   
  2296   2297     if( rc==SQLITE_OK ){
  2297   2298       if( ii!=pPhrase->nToken ){
  2298   2299         assert( pCsr->eEvalmode==FTS3_EVAL_FILTER && isReqPos==0 );
  2299   2300         fts3DoclistStripPositions(pOut, &nOut);
  2300   2301       }

Changes to test/fts3cov.test.

    15     15   # If this build does not include FTS3, skip the tests in this file.
    16     16   #
    17     17   ifcapable !fts3 { finish_test ; return }
    18     18   source $testdir/fts3_common.tcl
    19     19   source $testdir/malloc_common.tcl
    20     20   
    21     21   set DO_MALLOC_TEST 0
           22  +set testprefix fts3cov
    22     23   
    23     24   #--------------------------------------------------------------------------
    24     25   # When it first needs to read a block from the %_segments table, the FTS3 
    25     26   # module compiles an SQL statement for that purpose. The statement is 
    26     27   # stored and reused each subsequent time a block is read. This test case 
    27     28   # tests the effects of an OOM error occuring while compiling the statement.
    28     29   #
................................................................................
   364    365     INSERT INTO t13 VALUES('scalar two functions');
   365    366     INSERT INTO t13 VALUES('functions scalar two');
   366    367   } -sqlbody {
   367    368     SELECT snippet(t13, '%%', '%%', '#') FROM t13 WHERE t13 MATCH 'two';
   368    369     SELECT snippet(t13, '%%', '%%') FROM t13 WHERE t13 MATCH 'two';
   369    370     SELECT snippet(t13, '%%') FROM t13 WHERE t13 MATCH 'two';
   370    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  +
   371    419   
   372    420   finish_test

Changes to test/fts3defer.test.

   206    206     }
   207    207     2 {
   208    208       set dmt_modes 0
   209    209       execsql { CREATE VIRTUAL TABLE t1 USING FTS4 }
   210    210       foreach doc $data { execsql { INSERT INTO t1 VALUES($doc) } }
   211    211     }
   212    212     3 {
   213         -    set dmt_modes {0 1 2}
          213  +    #set dmt_modes {0 1 2}
   214    214       execsql { CREATE VIRTUAL TABLE t1 USING FTS4 }
   215    215       foreach doc $data { execsql { INSERT INTO t1 VALUES($doc) } }
   216    216       execsql $zero_long_doclists
   217    217     }
   218    218     4 {
   219    219       set dmt_modes 0
   220    220       execsql { CREATE VIRTUAL TABLE t1 USING FTS4 }
................................................................................
   256    256     } {12 13 29 30 40 47 48 52 63 92 93}
   257    257     do_select_test 1.8 {
   258    258       SELECT rowid FROM t1 WHERE t1 MATCH 'zm eh'
   259    259     } {68 100}
   260    260     do_select_test 1.9 {
   261    261       SELECT rowid FROM t1 WHERE t1 MATCH 'zm ubwrfqnbjf'
   262    262     } {7 70 98}
          263  +  do_select_test 1.10 {
          264  +    SELECT rowid FROM t1 WHERE t1 MATCH 'z* vgsld'
          265  +  } {10 13 17 31 35 51 58 88 89 90 93 100}
          266  +  do_select_test 1.11 {
          267  +    SELECT rowid FROM t1 
          268  +    WHERE t1 MATCH '(
          269  +      zdu OR zexh OR zf OR zhbrzadb OR zidhxhbtv OR 
          270  +      zk OR zkhdvkw OR zm OR zsmhnf
          271  +    ) vgsld'
          272  +  } {10 13 17 31 35 51 58 88 89 90 93 100}
   263    273   
   264    274     do_select_test 2.1 {
   265    275       SELECT rowid FROM t1 WHERE t1 MATCH '"zm agmckuiu"'
   266    276     } {3 24 52 53}
   267    277     do_select_test 2.2 {
   268    278       SELECT rowid FROM t1 WHERE t1 MATCH '"zm zf"'
   269    279     } {33 53 75 88 101}
................................................................................
   278    288     } {16 53}
   279    289     do_select_test 2.6 {
   280    290       SELECT rowid FROM t1 WHERE t1 MATCH '"xh jk jk"'
   281    291     } {18}
   282    292     do_select_test 2.7 {
   283    293       SELECT rowid FROM t1 WHERE t1 MATCH '"zm jk vgsld"'
   284    294     } {13 17}
   285         -
   286    295     do_select_test 2.8 {
   287         -    SELECT rowid FROM t1 WHERE t1 MATCH 'z* vgsld'
   288         -  } {10 13 17 31 35 51 58 88 89 90 93 100}
   289         -  do_select_test 2.9 {
   290         -    SELECT rowid FROM t1 
   291         -    WHERE t1 MATCH '(
   292         -      zdu OR zexh OR zf OR zhbrzadb OR zidhxhbtv OR 
   293         -      zk OR zkhdvkw OR zm OR zsmhnf
   294         -    ) vgsld'
   295         -  } {10 13 17 31 35 51 58 88 89 90 93 100}
          296  +    SELECT rowid FROM t1 WHERE t1 MATCH '"zm jk vgsld lkjlkjlkj"'
          297  +  } {}
   296    298   
   297    299     do_select_test 3.1 {
   298    300       SELECT snippet(t1, '[', ']') FROM t1 WHERE t1 MATCH '"zm agmckuiu"'
   299    301     } {
   300    302       {zm [zm] [agmckuiu] uhzq nsab jk rrkx duszemmzl hyq jk} 
   301    303       {jk [zm] [agmckuiu] urvysbnykk jk jk zm zm jk jk} 
   302    304       {[zm] [agmckuiu] zexh fibokdry jk uhzq bu tugflixoex xnxhf sk} 
................................................................................
   385    387       set DO_MALLOC_TEST 0
   386    388     }
   387    389   
   388    390     do_select_test 6.1 {
   389    391       SELECT rowid FROM t1 
   390    392       WHERE t1 MATCH 'vgsld (hrlipdm OR (aayxpmve duszemmzl))'
   391    393     } {10}
          394  +  do_select_test 6.2.1 {
          395  +    SELECT rowid FROM t1 WHERE t1 MATCH '"jk jcpiwj"'
          396  +  } {39 40}
          397  +  do_select_test 6.2.2 {
          398  +    SELECT rowid FROM t1 WHERE t1 MATCH '"zm xnxhf"'
          399  +  } {40 47}
          400  +  do_select_test 6.2.3 {
          401  +    SELECT rowid FROM t1 WHERE t1 MATCH '"jk jcpiwj" OR "zm xnxhf"'
          402  +  } {39 40 47}
          403  +}
          404  +
          405  +set testprefix fts3defer
          406  +
          407  +do_execsql_test 3.1 {
          408  +  CREATE VIRTUAL TABLE x1 USING fts4(a, b);
          409  +  INSERT INTO x1 VALUES('a b c', 'd e f');
          410  +  INSERT INTO x1 SELECT * FROM x1;
          411  +  INSERT INTO x1 SELECT * FROM x1;
          412  +  INSERT INTO x1 SELECT * FROM x1;
          413  +  INSERT INTO x1 SELECT * FROM x1;
   392    414   }
          415  +do_execsql_test 3.2 "
          416  +  INSERT INTO x1 VALUES(
          417  +    '[string repeat {d } 3000]', '[string repeat {f } 30000]'
          418  +  );
          419  +  INSERT INTO x1(x1) VALUES('optimize');
          420  +"
          421  +
          422  +do_execsql_test 3.3 {
          423  +  SELECT count(*) FROM x1 WHERE x1 MATCH '"d e f"'
          424  +} {16}
   393    425   
   394    426   
   395    427   finish_test

Added test/fts3fault.test.

            1  +# 2010 June 15
            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  +
           13  +set testdir [file dirname $argv0]
           14  +source $testdir/tester.tcl
           15  +
           16  +set ::testprefix fts3fault
           17  +
           18  +# Test error handling in the sqlite3Fts3Init() function. This is the 
           19  +# function that registers the FTS3 module and various support functions
           20  +# with SQLite.
           21  +#
           22  +do_faultsim_test 1 -body { 
           23  +  sqlite3 db test.db 
           24  +  expr 0
           25  +} -test {
           26  +  catch { db close }
           27  +}
           28  +
           29  +# Test error handling in an "ALTER TABLE ... RENAME TO" statement on an
           30  +# FTS3 table. Specifically, test renaming the table within a transaction
           31  +# after it has been written to.
           32  +#
           33  +faultsim_delete_and_reopen
           34  +do_execsql_test 2.0 {
           35  +  CREATE VIRTUAL TABLE t1 USING fts3;
           36  +  INSERT INTO t1 VALUES('test renaming the table');
           37  +  INSERT INTO t1 VALUES(' after it has been written');
           38  +}
           39  +do_faultsim_test 2 -prep { 
           40  +  sqlite3 db test.db
           41  +  execsql {
           42  +    BEGIN;
           43  +      INSERT INTO t1 VALUES('registers the FTS3 module');
           44  +      INSERT INTO t1 VALUES('various support functions');
           45  +  }
           46  +} -body {
           47  +  execsql { ALTER TABLE t1 RENAME TO t2 }
           48  +} -test {
           49  +  faultsim_test_result {0 {}} 
           50  +}
           51  +
           52  +# Test error handling in the special case where a single prefix query 
           53  +# matches terms that reside on a large range of leaf nodes.
           54  +#
           55  +do_test fts3fault-3.0 {
           56  +  sqlite3 db test.db
           57  +  execsql { CREATE VIRTUAL TABLE t3 USING fts4; }
           58  +  execsql { INSERT INTO t3(t3) VALUES('nodesize=50') }
           59  +  execsql { BEGIN }
           60  +  for {set i 0} {$i < 1000} {incr i} {
           61  +    execsql { INSERT INTO t3 VALUES('aaa' || $i) }
           62  +  }
           63  +  execsql { COMMIT }
           64  +} {}
           65  +
           66  +do_faultsim_test 3 -faults oom-transient -prep { 
           67  +  sqlite3 db test.db
           68  +  execsql { SELECT * FROM t3 WHERE t3 MATCH 'x' }
           69  +} -body {
           70  +  execsql { SELECT count(rowid) FROM t3 WHERE t3 MATCH 'aa*' }
           71  +} -test {
           72  +  faultsim_test_result {0 1000} 
           73  +}
           74  +
           75  +do_test fts3fault-4.0 {
           76  +  faultsim_delete_and_reopen
           77  +  execsql { 
           78  +    CREATE VIRTUAL TABLE t4 USING fts4; 
           79  +    INSERT INTO t4 VALUES('The British Government called on');
           80  +    INSERT INTO t4 VALUES('as pesetas then became much');
           81  +  }
           82  +} {}
           83  +faultsim_save_and_close
           84  +do_faultsim_test 4 -prep { 
           85  +  faultsim_restore_and_reopen
           86  +  execsql { SELECT content FROM t4 }
           87  +} -body {
           88  +  execsql { SELECT optimize(t4) FROM t4 LIMIT 1 }
           89  +} -test {
           90  +  faultsim_test_result {0 {{Index optimized}}}
           91  +}
           92  +
           93  +do_test fts3fault-5.0 {
           94  +  faultsim_delete_and_reopen
           95  +  execsql { 
           96  +    CREATE VIRTUAL TABLE t5 USING fts4; 
           97  +    INSERT INTO t5 VALUES('The British Government called on');
           98  +    INSERT INTO t5 VALUES('as pesetas then became much');
           99  +  }
          100  +} {}
          101  +faultsim_save_and_close
          102  +do_faultsim_test 5 -prep { 
          103  +  faultsim_restore_and_reopen
          104  +  execsql { 
          105  +    BEGIN;
          106  +      INSERT INTO t5 VALUES('influential in shaping his future outlook');
          107  +      INSERT INTO t5 VALUES('might be acceptable to the British electorate');
          108  +  }
          109  +} -body {
          110  +  execsql { SELECT rowid FROM t5 WHERE t5 MATCH 'british' }
          111  +} -test {
          112  +  faultsim_test_result {0 {1 4}}
          113  +}
          114  +
          115  +do_test fts3fault-6.0 {
          116  +  faultsim_delete_and_reopen
          117  +  execsql { CREATE VIRTUAL TABLE t6 USING fts4 }
          118  +} {}
          119  +faultsim_save_and_close
          120  +do_faultsim_test 6 -prep { 
          121  +  faultsim_restore_and_reopen
          122  +  execsql { SELECT rowid FROM t6 }
          123  +} -body {
          124  +  execsql { DROP TABLE t6 }
          125  +} -test {
          126  +  faultsim_test_result {0 {}}
          127  +}
          128  +
          129  +finish_test

Changes to test/fts3query.test.

   169    169   
   170    170   # Test the snippet() function with 1 to 6 arguments.
   171    171   # 
   172    172   do_execsql_test 6.1 {
   173    173     CREATE VIRTUAL TABLE t3 USING FTS4(a, b);
   174    174     INSERT INTO t3 VALUES('no gestures', 'another intriguing discovery by observing the hand gestures (called beats) people make while speaking. Research has shown that such gestures do more than add visual emphasis to our words (many people gesture while they''re on the telephone, for example); it seems they actually help our brains find words');
   175    175   }
   176         -
   177    176   do_select_tests 6.2 {
   178    177     1 "SELECT snippet(t3) FROM t3 WHERE t3 MATCH 'gestures'"
   179    178     {{<b>...</b>hand <b>gestures</b> (called beats) people make while speaking. Research has shown that such <b>gestures</b> do<b>...</b>}}
   180    179   
   181    180     2 "SELECT snippet(t3, 'XXX') FROM t3 WHERE t3 MATCH 'gestures'" 
   182    181     {{<b>...</b>hand XXXgestures</b> (called beats) people make while speaking. Research has shown that such XXXgestures</b> do<b>...</b>}}
   183    182   
................................................................................
   192    191   
   193    192     6 "SELECT snippet(t3, 'XXX', 'YYY', 'ZZZ', 0) FROM t3 WHERE t3 MATCH 'gestures'" 
   194    193     {{no XXXgesturesYYY}}
   195    194   
   196    195     7 "SELECT snippet(t3, 'XXX', 'YYY', 'ZZZ', 1, 5) FROM t3 WHERE t3 MATCH 'gestures'" 
   197    196     {{ZZZthe hand XXXgesturesYYY (called beatsZZZ}}
   198    197   }
          198  +
   199    199   
   200    200   finish_test
   201    201