/ Check-in [a4c35fa2]
Login
SQLite training in Houston TX on 2019-11-05 (details)
Part of the 2019 Tcl Conference

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

Overview
Comment:Fix a problem with fts5 synonyms and phrase queries. Also fix an OOM handling bug in fts5.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | fts5-incompatible
Files: files | file ages | folders
SHA1: a4c35fa2c94fe34b376670244fe72303c99868c1
User & Date: dan 2015-09-02 17:34:22
Context
2015-09-02
18:56
Fix an issue with fts5 synonyms and NEAR(...) queries. check-in: f2e59070 user: dan tags: fts5-incompatible
17:34
Fix a problem with fts5 synonyms and phrase queries. Also fix an OOM handling bug in fts5. check-in: a4c35fa2 user: dan tags: fts5-incompatible
14:17
Fix a problem handling OOM conditions within fts5 queries that feature synonyms. check-in: 11fa9808 user: dan tags: fts5-incompatible
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to ext/fts5/fts5_expr.c.

   284    284     return 0;
   285    285   }
   286    286   
   287    287   /*
   288    288   ** Argument pTerm must be a synonym iterator. Return the current rowid
   289    289   ** that it points to.
   290    290   */
   291         -static i64 fts5ExprSynonymRowid(Fts5ExprTerm *pTerm, int bDesc){
   292         -  i64 iRet;
          291  +static i64 fts5ExprSynonymRowid(Fts5ExprTerm *pTerm, int bDesc, int *pbEof){
          292  +  i64 iRet = 0;
   293    293     int bRetValid = 0;
   294    294     Fts5ExprTerm *p;
   295    295   
   296    296     assert( pTerm->pSynonym );
   297    297     assert( bDesc==0 || bDesc==1 );
   298    298     for(p=pTerm; p; p=p->pSynonym){
   299    299       if( 0==sqlite3Fts5IterEof(p->pIter) ){
................................................................................
   301    301         if( bRetValid==0 || (bDesc!=(iRowid<iRet)) ){
   302    302           iRet = iRowid;
   303    303           bRetValid = 1;
   304    304         }
   305    305       }
   306    306     }
   307    307   
   308         -  assert( bRetValid );
          308  +  if( pbEof && bRetValid==0 ) *pbEof = 1;
   309    309     return iRet;
   310    310   }
   311    311   
   312    312   /*
   313    313   ** Argument pTerm must be a synonym iterator.
   314    314   */
   315    315   static int fts5ExprSynonymPoslist(
................................................................................
   342    342             rc = SQLITE_NOMEM;
   343    343             goto synonym_poslist_out;
   344    344           }
   345    345           memcpy(aNew, aIter, sizeof(Fts5PoslistReader) * nIter);
   346    346           nAlloc = nAlloc*2;
   347    347           aIter = aNew;
   348    348         }
   349         -      if( sqlite3Fts5PoslistReaderInit(-1, a, n, &aIter[nIter])==0 ){
   350         -        nIter++;
   351         -      }
          349  +      sqlite3Fts5PoslistReaderInit(-1, a, n, &aIter[nIter]);
          350  +      assert( aIter[nIter].bEof==0 );
          351  +      nIter++;
   352    352       }
   353    353     }
   354    354   
   355    355     assert( *pbDel==0 );
   356    356     if( nIter==1 ){
   357    357       *pa = (u8*)aIter[0].a;
   358    358       *pn = aIter[0].n;
................................................................................
   655    655     int rc;
   656    656   
   657    657     if( pTerm->pSynonym ){
   658    658       int bEof = 1;
   659    659       Fts5ExprTerm *p;
   660    660   
   661    661       /* Find the firstest rowid any synonym points to. */
   662         -    i64 iRowid = fts5ExprSynonymRowid(pTerm, pExpr->bDesc);
          662  +    i64 iRowid = fts5ExprSynonymRowid(pTerm, pExpr->bDesc, 0);
   663    663   
   664    664       /* Advance each iterator that currently points to iRowid. Or, if iFrom
   665    665         ** is valid - each iterator that points to a rowid before iFrom.  */
   666    666       for(p=pTerm; p; p=p->pSynonym){
   667    667         if( sqlite3Fts5IterEof(p->pIter)==0 ){
   668    668           i64 ii = sqlite3Fts5IterRowid(p->pIter);
   669    669           if( ii==iRowid 
................................................................................
   733    733       iRowid = sqlite3Fts5IterRowid(pIter);
   734    734       assert( (bDesc==0 && iRowid>=iLast) || (bDesc==1 && iRowid<=iLast) );
   735    735     }
   736    736     *piLast = iRowid;
   737    737   
   738    738     return 0;
   739    739   }
          740  +
          741  +static int fts5ExprSynonymAdvanceto(
          742  +  Fts5ExprTerm *pTerm,            /* Term iterator to advance */
          743  +  int bDesc,                      /* True if iterator is "rowid DESC" */
          744  +  i64 *piLast,                    /* IN/OUT: Lastest rowid seen so far */
          745  +  int *pRc,                       /* OUT: Error code */
          746  +  int *pbEof                      /* OUT: Set to true if EOF */
          747  +){
          748  +  int rc = SQLITE_OK;
          749  +  i64 iLast = *piLast;
          750  +  Fts5ExprTerm *p;
          751  +
          752  +  for(p=pTerm; rc==SQLITE_OK && p; p=p->pSynonym){
          753  +    if( sqlite3Fts5IterEof(p->pIter)==0 ){
          754  +      i64 iRowid = sqlite3Fts5IterRowid(p->pIter);
          755  +      if( (bDesc==0 && iLast>iRowid) || (bDesc && iLast<iRowid) ){
          756  +        rc = sqlite3Fts5IterNextFrom(p->pIter, iLast);
          757  +      }
          758  +    }
          759  +  }
          760  +
          761  +  if( rc!=SQLITE_OK ){
          762  +    *pbEof = 1;
          763  +  }else{
          764  +    *piLast = fts5ExprSynonymRowid(pTerm, bDesc, pbEof);
          765  +  }
          766  +
          767  +  return rc;
          768  +}
   740    769   
   741    770   /*
   742    771   ** IN/OUT parameter (*pa) points to a position list n bytes in size. If
   743    772   ** the position list contains entries for column iCol, then (*pa) is set
   744    773   ** to point to the sub-position-list for that column and the number of
   745    774   ** bytes in it returned. Or, if the argument position list does not
   746    775   ** contain any entries for column iCol, return 0.
................................................................................
   902    931     );
   903    932   
   904    933     /* Initialize iLast, the "lastest" rowid any iterator points to. If the
   905    934     ** iterator skips through rowids in the default ascending order, this means
   906    935     ** the maximum rowid. Or, if the iterator is "ORDER BY rowid DESC", then it
   907    936     ** means the minimum rowid.  */
   908    937     if( pLeft->aTerm[0].pSynonym ){
   909         -    iLast = fts5ExprSynonymRowid(&pLeft->aTerm[0], bDesc);
          938  +    iLast = fts5ExprSynonymRowid(&pLeft->aTerm[0], bDesc, 0);
   910    939     }else{
   911    940       iLast = sqlite3Fts5IterRowid(pLeft->aTerm[0].pIter);
   912    941     }
   913    942   
   914    943     do {
   915    944       bMatch = 1;
   916    945       for(i=0; i<pNear->nPhrase; i++){
   917    946         Fts5ExprPhrase *pPhrase = pNear->apPhrase[i];
   918    947         for(j=0; j<pPhrase->nTerm; j++){
   919    948           Fts5ExprTerm *pTerm = &pPhrase->aTerm[j];
   920    949           if( pTerm->pSynonym ){
   921    950             Fts5ExprTerm *p;
   922    951             int bEof = 1;
   923         -          i64 iRowid = fts5ExprSynonymRowid(pTerm, bDesc);
          952  +          i64 iRowid = fts5ExprSynonymRowid(pTerm, bDesc, 0);
   924    953             if( iRowid==iLast ) continue;
   925         -          for(p=pTerm; p; p=p->pSynonym){
   926         -            Fts5IndexIter *pIter = p->pIter;
   927         -            int dummy;
   928         -            if( 0==sqlite3Fts5IterEof(pIter)
   929         -             && 0==fts5ExprAdvanceto(pIter, bDesc, &iLast, &rc, &dummy)==0 
   930         -            ){
   931         -              bEof = 0;
   932         -            }
   933         -          }
   934         -          if( bEof || rc ){
   935         -            pNode->bEof = 1;
          954  +          bMatch = 0;
          955  +          if( fts5ExprSynonymAdvanceto(pTerm,bDesc,&iLast,&rc,&pNode->bEof) ){
   936    956               return rc;
   937    957             }
          958  +
   938    959           }else{
   939    960             Fts5IndexIter *pIter = pPhrase->aTerm[j].pIter;
   940    961             i64 iRowid = sqlite3Fts5IterRowid(pIter);
   941    962             if( iRowid==iLast ) continue;
   942    963             bMatch = 0;
   943    964             if( fts5ExprAdvanceto(pIter, bDesc, &iLast, &rc, &pNode->bEof) ){
   944    965               return rc;

Changes to ext/fts5/test/fts5fault6.test.

    17     17   set testprefix fts5fault6
    18     18   
    19     19   # If SQLITE_ENABLE_FTS5 is defined, omit this file.
    20     20   ifcapable !fts5 {
    21     21     finish_test
    22     22     return
    23     23   }
           24  +
           25  +if 1 {
    24     26   
    25     27   #-------------------------------------------------------------------------
    26     28   # OOM while rebuilding an FTS5 table.
    27     29   #
    28     30   do_execsql_test 1.0 {
    29     31     CREATE VIRTUAL TABLE tt USING fts5(a, b);
    30     32     INSERT INTO tt VALUES('c d c g g f', 'a a a d g a');
................................................................................
   143    145   } -body {
   144    146     db eval { 
   145    147       CREATE VIRTUAL TABLE yu USING fts5(x, tokenize="unicode61 separators abc");
   146    148     }
   147    149   } -test {
   148    150     faultsim_test_result {0 {}}
   149    151   }
          152  +
          153  +}
   150    154   
   151    155   #-------------------------------------------------------------------------
   152         -# OOM while running a query that includes synonyms and matchinfo().
          156  +#
          157  +# 5.2.* OOM while running a query that includes synonyms and matchinfo().
          158  +#
          159  +# 5.3.* OOM while running a query that returns a row containing instances
          160  +#       of more than 4 synonyms for a single term.
   153    161   #
   154    162   proc mit {blob} {
   155    163     set scan(littleEndian) i*
   156    164     set scan(bigEndian) I*
   157    165     binary scan $blob $scan($::tcl_platform(byteOrder)) r
   158    166     return $r
   159    167   }
................................................................................
   170    178   proc tcl_create {args} { return "tcl_tokenize" }
   171    179   reset_db
   172    180   sqlite3_fts5_create_tokenizer db tcl tcl_create
   173    181   db func mit mit
   174    182   sqlite3_fts5_register_matchinfo db
   175    183   do_test 5.0 {
   176    184     execsql { CREATE VIRTUAL TABLE t1 USING fts5(a, tokenize=tcl) }
          185  +  execsql { INSERT INTO t1(t1, rank) VALUES('pgsz', 32) }
   177    186     foreach {rowid text} {
   178    187       1 {aaaa cc b aaaaa cc aa} 
   179    188       2 {aa aa bb a bbb}
   180    189       3 {bb aaaaa aaaaa b aaaa aaaaa}
   181    190       4 {aa a b aaaa aa}
   182    191       5 {aa b ccc aaaaa cc}
   183    192       6 {aa aaaaa bbbb cc aaa}
   184    193       7 {aaaaa aa aa ccccc bb}
   185    194       8 {ccc bbbbb ccccc bbb c}
   186    195       9 {cccccc bbbb a aaa cccc c}
          196  +
          197  +    20 {ddd f ddd eeeee fff ffff eeee ddd fff eeeee dddddd eeee}
          198  +    21 {fffff eee dddd fffff dd ee ee eeeee eee eeeeee ee dd e}
          199  +    22 {fffff d eeee dddd fffff dddddd ffff ddddd eeeee ee eee dddd ddddd}
          200  +    23 {ddddd fff ddd eeeee ffff eeee ddd ff ff ffffff eeeeee dddd ffffff}
          201  +    24 {eee dd ee dddd dddd eeeeee e eee fff ffff}
          202  +    25 {ddddd ffffff dddddd fff ddd ddddd ddd f eeee fff dddd f}
          203  +    26 {f ffff fff fff eeeeee dddd d dddddd ddddd eee ff eeeee}
          204  +    27 {eee fff dddddd eeeee eeeee dddd ddddd ffff f eeeee eee dddddd ddddd d}
          205  +    28 {dd ddddd d ddd d fff d dddd ee dddd ee ddd dddddd dddddd}
          206  +    29 {eeee dddd ee dddd eeee dddd dd fffff f ddd eeeee ddd ee}
          207  +    30 {ff ffffff eeeeee eeeee eee ffffff ff ffff f fffff eeeee}
          208  +    31 {fffff eeeeee dddd eeee eeee eeeeee eee fffff d ddddd ffffff ffff dddddd}
          209  +    32 {dddddd fffff ee eeeeee eeee ee fff dddd fff eeee ffffff eeeeee ffffff}
          210  +    33 {ddddd eeee dd ffff dddddd fff eeee ddddd ffff eeee ddd}
          211  +    34 {ee dddd ddddd dddddd eeee eeeeee f dd ee dddddd ffffff}
          212  +    35 {ee dddd dd eeeeee ddddd eee d eeeeee dddddd eee dddd fffff}
          213  +    36 {eee ffffff ffffff e fffff eeeee ff dddddd dddddd fff}
          214  +    37 {eeeee fffff dddddd dddd ffffff fff f dd ee dd dd eeeee}
          215  +    38 {eeeeee ee d ff eeeeee eeeeee eee eeeee ee ffffff dddd eeee dddddd ee}
          216  +    39 {eeeeee ddd fffff e dddd ee eee eee ffffff ee f d dddd}
          217  +    40 {ffffff dddddd eee ee ffffff eee eeee ddddd ee eeeeee f}
          218  +    41 {ddd ddd fff fffff ee fffff f fff ddddd fffff}
          219  +    42 {dddd ee ff d f ffffff fff ffffff ff dd dddddd f eeee}
          220  +    43 {d dd fff fffff d f fff e dddd ee ee}
          221  +    44 {ff ffff eee ddd d dd ffff dddd d eeee d eeeeee}
          222  +    45 {eeee f eeeee ee e ffff f ddd e fff}
          223  +    46 {ffff d ffff eeee ffff eeeee f ffff ddddd eee}
          224  +    47 {dd dd dddddd ddddd fffff dddddd ddd ddddd eeeeee ffff eeee eee ee}
          225  +    48 {ffff ffff e dddd ffffff dd dd dddd f fffff}
          226  +    49 {ffffff d dddddd ffff eeeee f ffff ffff d dd fffff eeeee}
   187    227     } {
   188    228       execsql { INSERT INTO t1(rowid, a) VALUES($rowid, $text) }
   189    229     }
   190    230   } {}
   191    231   
   192    232   set res [list {*}{
   193    233     1 {3 24 8 2 12 6}
   194    234     5 {2 24 8 2 12 6}
   195    235     6 {3 24 8 1 12 6}
   196    236     7 {3 24 8 1 12 6}
   197    237     9 {2 24 8 3 12 6}
   198    238   }]
   199         -do_execsql_test 5.1 {
          239  +do_execsql_test 5.1.1 {
   200    240     SELECT rowid, mit(matchinfo(t1, 'x')) FROM t1 WHERE t1 MATCH 'a AND c'
   201    241   } $res
          242  +do_execsql_test 5.1.2 {
          243  +  SELECT count(*) FROM t1 WHERE t1 MATCH 'd e f'
          244  +} 29
   202    245   
   203    246   faultsim_save_and_close
   204    247   do_faultsim_test 5.2 -faults oom* -prep {
   205    248     faultsim_restore_and_reopen
   206    249     sqlite3_fts5_create_tokenizer db tcl tcl_create
   207    250     sqlite3_fts5_register_matchinfo db
   208    251     db func mit mit
................................................................................
   209    252   } -body {
   210    253     db eval { 
   211    254       SELECT rowid, mit(matchinfo(t1, 'x')) FROM t1 WHERE t1 MATCH 'a AND c'
   212    255     }
   213    256   } -test {
   214    257     faultsim_test_result [list 0 $::res]
   215    258   }
          259  +
          260  +do_faultsim_test 5.3 -faults oom* -prep {
          261  +  faultsim_restore_and_reopen
          262  +  sqlite3_fts5_create_tokenizer db tcl tcl_create
          263  +} -body {
          264  +  db eval { 
          265  +    SELECT count(*) FROM t1 WHERE t1 MATCH 'd AND e AND f'
          266  +  }
          267  +} -test {
          268  +  faultsim_test_result {0 29}
          269  +}
   216    270   
   217    271   finish_test
   218    272   

Changes to ext/fts5/test/fts5synonym.test.

    89     89   } {}
    90     90   
    91     91   foreach {tn expr res} {
    92     92     1 "3" 1
    93     93     2 "eight OR 8 OR 5" {2 3}
    94     94     3 "10" {}
    95     95     4 "1*" {1}
           96  +  5 "1 + 2" {1}
    96     97   } {
    97     98     do_execsql_test 2.1.$tn {
    98     99       SELECT rowid FROM ft WHERE ft MATCH $expr
    99    100     } $res
   100    101   }
   101    102   
   102    103   #-------------------------------------------------------------------------
................................................................................
   228    229       7 {ii ii two three 2 5} {iii i ii iii iii one one}
   229    230       8 {2 ii i two 3 three 2} {two iv v iii 3 five}
   230    231       9 {i 2 iv 3 five four v} {iii 4 three i three ii 1}
   231    232     } {
   232    233       execsql { INSERT INTO t1(rowid, a, b) VALUES($rowid, $a, $b) }
   233    234     }
   234    235   } {}
          236  +
   235    237   
   236    238   foreach {tn q res} {
   237    239     1 {one} {
   238    240       1 {four v 4 [i] three} {[1] 3 five five 4 [one]}
   239    241       2 {5 [1] 3 4 [i]} {2 2 v two 4}
   240    242       3 {5 [i] 5 2 four 4 [1]} {iii ii five two [1]}
   241    243       4 {ii four 4 [one] 5 three five} {[one] 5 [1] iii 4 3}
................................................................................
   261    263       4 {[ii] [four] [4] [one] [5] [three] [five]} {[one] [5] [1] [iii] [4] [3]}
   262    264       5 {[three] [i] [v] [i] [four] [4] [1]} {[ii] [five] [five] [five] [iii]}
   263    265       6 {[4] [2] [ii] [two] [2] [iii]} {[three] [1] [four] [4] [iv] [1] [iv]}
   264    266       7 {[ii] [ii] [two] [three] [2] [5]} {[iii] [i] [ii] [iii] [iii] [one] [one]}
   265    267       8 {[2] [ii] [i] [two] [3] [three] [2]} {[two] [iv] [v] [iii] [3] [five]}
   266    268       9 {[i] [2] [iv] [3] [five] [four] [v]} {[iii] [4] [three] [i] [three] [ii] [1]}
   267    269     }
          270  +
          271  +  4 {5 + 1} {
          272  +    2 {[5 1] 3 4 i} {2 2 v two 4} 
          273  +    3 {[5 i] 5 2 four 4 1} {iii ii five two 1} 
          274  +    4 {ii four 4 one 5 three five} {one [5 1] iii 4 3} 
          275  +    5 {three i [v i] four 4 1} {ii five five five iii}
          276  +  }
          277  +
          278  +  5 {one + two + three} {
          279  +    7 {ii ii two three 2 5} {iii [i ii iii] iii one one}
          280  +    8 {2 ii [i two 3] three 2} {two iv v iii 3 five}
          281  +  }
          282  +
          283  +  6 {"v v"} {
          284  +    1 {four v 4 i three} {1 3 [five five] 4 one}
          285  +    5 {three i v i four 4 1} {ii [five five five] iii}
          286  +  }
   268    287   } {
   269    288     do_execsql_test 5.1.$tn {
   270    289       SELECT rowid, highlight(t1, 0, '[', ']'), highlight(t1, 1, '[', ']')
   271    290       FROM t1 WHERE t1 MATCH $q
   272    291     } $res
   273    292   }
   274    293   

Changes to main.mk.

    43     43   ################################################################################
    44     44   
    45     45   # This is how we compile
    46     46   #
    47     47   TCCX =  $(TCC) $(OPTS) -I. -I$(TOP)/src -I$(TOP) 
    48     48   TCCX += -I$(TOP)/ext/rtree -I$(TOP)/ext/icu -I$(TOP)/ext/fts3
    49     49   TCCX += -I$(TOP)/ext/async -I$(TOP)/ext/userauth
           50  +TCCX += -I$(TOP)/ext/fts5
    50     51   
    51     52   # Object files for the SQLite library.
    52     53   #
    53     54   LIBOBJ+= vdbe.o parse.o \
    54     55            alter.o analyze.o attach.o auth.o \
    55     56            backup.o bitvec.o btmutex.o btree.o build.o \
    56     57            callback.o complete.o ctime.o date.o dbstat.o delete.o expr.o fault.o fkey.o \
................................................................................
   225    226     $(TOP)/ext/userauth/userauth.c \
   226    227     $(TOP)/ext/userauth/sqlite3userauth.h 
   227    228   
   228    229   SRC += \
   229    230     $(TOP)/ext/rbu/sqlite3rbu.c \
   230    231     $(TOP)/ext/rbu/sqlite3rbu.h
   231    232   
          233  +
          234  +# FTS5 things
          235  +#
          236  +FTS5_HDR = \
          237  +   $(TOP)/ext/fts5/fts5.h \
          238  +   $(TOP)/ext/fts5/fts5Int.h \
          239  +   fts5parse.h
          240  +	   
          241  +FTS5_SRC = \
          242  +   $(TOP)/ext/fts5/fts5_aux.c \
          243  +   $(TOP)/ext/fts5/fts5_buffer.c \
          244  +   $(TOP)/ext/fts5/fts5_main.c \
          245  +   $(TOP)/ext/fts5/fts5_config.c \
          246  +   $(TOP)/ext/fts5/fts5_expr.c \
          247  +   $(TOP)/ext/fts5/fts5_hash.c \
          248  +   $(TOP)/ext/fts5/fts5_index.c \
          249  +   fts5parse.c \
          250  +   $(TOP)/ext/fts5/fts5_storage.c \
          251  +   $(TOP)/ext/fts5/fts5_tokenize.c \
          252  +   $(TOP)/ext/fts5/fts5_unicode2.c \
          253  +   $(TOP)/ext/fts5/fts5_varint.c \
          254  +   $(TOP)/ext/fts5/fts5_vocab.c  \
          255  +
   232    256   
   233    257   # Generated source code files
   234    258   #
   235    259   SRC += \
   236    260     keywordhash.h \
   237    261     opcodes.c \
   238    262     opcodes.h \
................................................................................
   632    656   
   633    657   fts3_write.o:	$(TOP)/ext/fts3/fts3_write.c $(HDR) $(EXTHDR)
   634    658   	$(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_write.c
   635    659   
   636    660   rtree.o:	$(TOP)/ext/rtree/rtree.c $(HDR) $(EXTHDR)
   637    661   	$(TCCX) -DSQLITE_CORE -c $(TOP)/ext/rtree/rtree.c
   638    662   
   639         -# FTS5 things
   640         -#
   641         -FTS5_SRC = \
   642         -   $(TOP)/ext/fts5/fts5.h \
   643         -   $(TOP)/ext/fts5/fts5Int.h \
   644         -   $(TOP)/ext/fts5/fts5_aux.c \
   645         -   $(TOP)/ext/fts5/fts5_buffer.c \
   646         -   $(TOP)/ext/fts5/fts5_main.c \
   647         -   $(TOP)/ext/fts5/fts5_config.c \
   648         -   $(TOP)/ext/fts5/fts5_expr.c \
   649         -   $(TOP)/ext/fts5/fts5_hash.c \
   650         -   $(TOP)/ext/fts5/fts5_index.c \
   651         -   fts5parse.c fts5parse.h \
   652         -   $(TOP)/ext/fts5/fts5_storage.c \
   653         -   $(TOP)/ext/fts5/fts5_tokenize.c \
   654         -   $(TOP)/ext/fts5/fts5_unicode2.c \
   655         -   $(TOP)/ext/fts5/fts5_varint.c \
   656         -   $(TOP)/ext/fts5/fts5_vocab.c  \
   657         -
   658    663   fts5parse.c:	$(TOP)/ext/fts5/fts5parse.y lemon 
   659    664   	cp $(TOP)/ext/fts5/fts5parse.y .
   660    665   	rm -f fts5parse.h
   661    666   	./lemon $(OPTS) fts5parse.y
   662    667   
   663    668   fts5parse.h: fts5parse.c
   664    669   
   665         -fts5.c: $(FTS5_SRC)
          670  +fts5.c: $(FTS5_SRC) $(FTS5_HDR)
   666    671   	tclsh $(TOP)/ext/fts5/tool/mkfts5c.tcl
   667    672   	cp $(TOP)/ext/fts5/fts5.h .
   668         -
   669    673   
   670    674   userauth.o:	$(TOP)/ext/userauth/userauth.c $(HDR) $(EXTHDR)
   671    675   	$(TCCX) -DSQLITE_CORE -c $(TOP)/ext/userauth/userauth.c
   672    676   
   673    677   sqlite3rbu.o:	$(TOP)/ext/rbu/sqlite3rbu.c $(HDR) $(EXTHDR)
   674    678   	$(TCCX) -DSQLITE_CORE -c $(TOP)/ext/rbu/sqlite3rbu.c
   675    679