/ Check-in [62ea9e5a]
Login

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

Overview
Comment:Enhance the performance of fts5 AND and OR queries.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 62ea9e5ab8bc1a20245beebceb5ea62dcd7ec84e
User & Date: dan 2016-02-02 17:40:41
Context
2016-02-02
21:19
Add tests to restore full coverage of fts5 code. check-in: 063755c8 user: dan tags: trunk
17:40
Enhance the performance of fts5 AND and OR queries. check-in: 62ea9e5a user: dan tags: trunk
02:04
Enhance the comment on the sqlite3_index_constraint object to bring attention to the fact than iColumn field can be negative for a rowid. check-in: d8b7b199 user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to ext/fts5/fts5Int.h.

   315    315   typedef struct Fts5Index Fts5Index;
   316    316   typedef struct Fts5IndexIter Fts5IndexIter;
   317    317   
   318    318   struct Fts5IndexIter {
   319    319     i64 iRowid;
   320    320     const u8 *pData;
   321    321     int nData;
          322  +  u8 bEof;
   322    323   };
          324  +
          325  +#define sqlite3Fts5IterEof(x) ((x)->bEof)
   323    326   
   324    327   /*
   325    328   ** Values used as part of the flags argument passed to IndexQuery().
   326    329   */
   327    330   #define FTS5INDEX_QUERY_PREFIX     0x0001   /* Prefix query */
   328    331   #define FTS5INDEX_QUERY_DESC       0x0002   /* Docs in descending rowid order */
   329    332   #define FTS5INDEX_QUERY_TEST_NOIDX 0x0004   /* Do not use prefix index */
................................................................................
   380    383     Fts5IndexIter **ppIter          /* OUT: New iterator object */
   381    384   );
   382    385   
   383    386   /*
   384    387   ** The various operations on open token or token prefix iterators opened
   385    388   ** using sqlite3Fts5IndexQuery().
   386    389   */
   387         -int sqlite3Fts5IterEof(Fts5IndexIter*);
   388    390   int sqlite3Fts5IterNext(Fts5IndexIter*);
   389    391   int sqlite3Fts5IterNextFrom(Fts5IndexIter*, i64 iMatch);
   390    392   i64 sqlite3Fts5IterRowid(Fts5IndexIter*);
   391    393   
   392    394   /*
   393    395   ** Close an iterator opened by sqlite3Fts5IndexQuery().
   394    396   */

Changes to ext/fts5/fts5_expr.c.

   299    299     int bRetValid = 0;
   300    300     Fts5ExprTerm *p;
   301    301   
   302    302     assert( pTerm->pSynonym );
   303    303     assert( bDesc==0 || bDesc==1 );
   304    304     for(p=pTerm; p; p=p->pSynonym){
   305    305       if( 0==sqlite3Fts5IterEof(p->pIter) ){
   306         -      i64 iRowid = sqlite3Fts5IterRowid(p->pIter);
          306  +      i64 iRowid = p->pIter->iRowid;
   307    307         if( bRetValid==0 || (bDesc!=(iRowid<iRet)) ){
   308    308           iRet = iRowid;
   309    309           bRetValid = 1;
   310    310         }
   311    311       }
   312    312     }
   313    313   
................................................................................
   332    332     int nAlloc = 4;
   333    333     int rc = SQLITE_OK;
   334    334     Fts5ExprTerm *p;
   335    335   
   336    336     assert( pTerm->pSynonym );
   337    337     for(p=pTerm; p; p=p->pSynonym){
   338    338       Fts5IndexIter *pIter = p->pIter;
   339         -    if( sqlite3Fts5IterEof(pIter)==0 && sqlite3Fts5IterRowid(pIter)==iRowid ){
          339  +    if( sqlite3Fts5IterEof(pIter)==0 && pIter->iRowid==iRowid ){
   340    340         if( pIter->nData==0 ) continue;
   341    341         if( nIter==nAlloc ){
   342    342           int nByte = sizeof(Fts5PoslistReader) * nAlloc * 2;
   343    343           Fts5PoslistReader *aNew = (Fts5PoslistReader*)sqlite3_malloc(nByte);
   344    344           if( aNew==0 ){
   345    345             rc = SQLITE_NOMEM;
   346    346             goto synonym_poslist_out;
................................................................................
   627    627       int bRet = a[0].pOut->n>0;
   628    628       *pRc = rc;
   629    629       if( a!=aStatic ) sqlite3_free(a);
   630    630       return bRet;
   631    631     }
   632    632   }
   633    633   
   634         -/*
   635         -** Advance the first term iterator in the first phrase of pNear. Set output
   636         -** variable *pbEof to true if it reaches EOF or if an error occurs.
   637         -**
   638         -** Return SQLITE_OK if successful, or an SQLite error code if an error
   639         -** occurs.
   640         -*/
   641         -static int fts5ExprNearAdvanceFirst(
   642         -  Fts5Expr *pExpr,                /* Expression pPhrase belongs to */
   643         -  Fts5ExprNode *pNode,            /* FTS5_STRING or FTS5_TERM node */
   644         -  int bFromValid,
   645         -  i64 iFrom 
   646         -){
   647         -  Fts5ExprTerm *pTerm = &pNode->pNear->apPhrase[0]->aTerm[0];
   648         -  int rc = SQLITE_OK;
   649         -
   650         -  pNode->bNomatch = 0;
   651         -  if( pTerm->pSynonym ){
   652         -    int bEof = 1;
   653         -    Fts5ExprTerm *p;
   654         -
   655         -    /* Find the firstest rowid any synonym points to. */
   656         -    i64 iRowid = fts5ExprSynonymRowid(pTerm, pExpr->bDesc, 0);
   657         -
   658         -    /* Advance each iterator that currently points to iRowid. Or, if iFrom
   659         -    ** is valid - each iterator that points to a rowid before iFrom.  */
   660         -    for(p=pTerm; p; p=p->pSynonym){
   661         -      if( sqlite3Fts5IterEof(p->pIter)==0 ){
   662         -        i64 ii = sqlite3Fts5IterRowid(p->pIter);
   663         -        if( ii==iRowid 
   664         -         || (bFromValid && ii!=iFrom && (ii>iFrom)==pExpr->bDesc) 
   665         -        ){
   666         -          if( bFromValid ){
   667         -            rc = sqlite3Fts5IterNextFrom(p->pIter, iFrom);
   668         -          }else{
   669         -            rc = sqlite3Fts5IterNext(p->pIter);
   670         -          }
   671         -          if( rc!=SQLITE_OK ) break;
   672         -          if( sqlite3Fts5IterEof(p->pIter)==0 ){
   673         -            bEof = 0;
   674         -          }
   675         -        }else{
   676         -          bEof = 0;
   677         -        }
   678         -      }
   679         -    }
   680         -
   681         -    /* Set the EOF flag if either all synonym iterators are at EOF or an
   682         -    ** error has occurred.  */
   683         -    pNode->bEof = (rc || bEof);
   684         -  }else{
   685         -    Fts5IndexIter *pIter = pTerm->pIter;
   686         -
   687         -    assert( Fts5NodeIsString(pNode) );
   688         -    if( bFromValid ){
   689         -      rc = sqlite3Fts5IterNextFrom(pIter, iFrom);
   690         -    }else{
   691         -      rc = sqlite3Fts5IterNext(pIter);
   692         -    }
   693         -
   694         -    pNode->bEof = (rc || sqlite3Fts5IterEof(pIter));
   695         -  }
   696         -
   697         -  return rc;
   698         -}
   699         -
   700    634   /*
   701    635   ** Advance iterator pIter until it points to a value equal to or laster
   702    636   ** than the initial value of *piLast. If this means the iterator points
   703    637   ** to a value laster than *piLast, update *piLast to the new lastest value.
   704    638   **
   705    639   ** If the iterator reaches EOF, set *pbEof to true before returning. If
   706    640   ** an error occurs, set *pRc to an error code. If either *pbEof or *pRc
................................................................................
   712    646     i64 *piLast,                    /* IN/OUT: Lastest rowid seen so far */
   713    647     int *pRc,                       /* OUT: Error code */
   714    648     int *pbEof                      /* OUT: Set to true if EOF */
   715    649   ){
   716    650     i64 iLast = *piLast;
   717    651     i64 iRowid;
   718    652   
   719         -  iRowid = sqlite3Fts5IterRowid(pIter);
          653  +  iRowid = pIter->iRowid;
   720    654     if( (bDesc==0 && iLast>iRowid) || (bDesc && iLast<iRowid) ){
   721    655       int rc = sqlite3Fts5IterNextFrom(pIter, iLast);
   722    656       if( rc || sqlite3Fts5IterEof(pIter) ){
   723    657         *pRc = rc;
   724    658         *pbEof = 1;
   725    659         return 1;
   726    660       }
   727         -    iRowid = sqlite3Fts5IterRowid(pIter);
          661  +    iRowid = pIter->iRowid;
   728    662       assert( (bDesc==0 && iRowid>=iLast) || (bDesc==1 && iRowid<=iLast) );
   729    663     }
   730    664     *piLast = iRowid;
   731    665   
   732    666     return 0;
   733    667   }
   734    668   
................................................................................
   741    675     int rc = SQLITE_OK;
   742    676     i64 iLast = *piLast;
   743    677     Fts5ExprTerm *p;
   744    678     int bEof = 0;
   745    679   
   746    680     for(p=pTerm; rc==SQLITE_OK && p; p=p->pSynonym){
   747    681       if( sqlite3Fts5IterEof(p->pIter)==0 ){
   748         -      i64 iRowid = sqlite3Fts5IterRowid(p->pIter);
          682  +      i64 iRowid = p->pIter->iRowid;
   749    683         if( (bDesc==0 && iLast>iRowid) || (bDesc && iLast<iRowid) ){
   750    684           rc = sqlite3Fts5IterNextFrom(p->pIter, iLast);
   751    685         }
   752    686       }
   753    687     }
   754    688   
   755    689     if( rc!=SQLITE_OK ){
................................................................................
   805    739       if( i==pNear->nPhrase && (i==1 || fts5ExprNearIsMatch(pRc, pNear)) ){
   806    740         return 1;
   807    741       }
   808    742       return 0;
   809    743     }
   810    744   }
   811    745   
   812         -static int fts5ExprTokenTest(
   813         -  Fts5Expr *pExpr,                /* Expression that pNear is a part of */
   814         -  Fts5ExprNode *pNode             /* The "NEAR" node (FTS5_TERM) */
   815         -){
   816         -  /* As this "NEAR" object is actually a single phrase that consists 
   817         -  ** of a single term only, grab pointers into the poslist managed by the
   818         -  ** fts5_index.c iterator object. This is much faster than synthesizing 
   819         -  ** a new poslist the way we have to for more complicated phrase or NEAR
   820         -  ** expressions.  */
   821         -  Fts5ExprPhrase *pPhrase = pNode->pNear->apPhrase[0];
   822         -  Fts5IndexIter *pIter = pPhrase->aTerm[0].pIter;
   823         -
   824         -  assert( pNode->eType==FTS5_TERM );
   825         -  assert( pNode->pNear->nPhrase==1 && pPhrase->nTerm==1 );
   826         -  assert( pPhrase->aTerm[0].pSynonym==0 );
   827         -
   828         -  pPhrase->poslist.n = pIter->nData;
   829         -  if( pExpr->pConfig->eDetail==FTS5_DETAIL_FULL ){
   830         -    pPhrase->poslist.p = (u8*)pIter->pData;
   831         -  }
   832         -  pNode->iRowid = pIter->iRowid;
   833         -  pNode->bNomatch = (pPhrase->poslist.n==0);
   834         -  return SQLITE_OK;
   835         -}
   836         -
   837         -/*
   838         -** All individual term iterators in pNear are guaranteed to be valid when
   839         -** this function is called. This function checks if all term iterators
   840         -** point to the same rowid, and if not, advances them until they do.
   841         -** If an EOF is reached before this happens, *pbEof is set to true before
   842         -** returning.
   843         -**
   844         -** SQLITE_OK is returned if an error occurs, or an SQLite error code 
   845         -** otherwise. It is not considered an error code if an iterator reaches
   846         -** EOF.
   847         -*/
   848         -static int fts5ExprNearNextMatch(
   849         -  Fts5Expr *pExpr,                /* Expression pPhrase belongs to */
   850         -  Fts5ExprNode *pNode
   851         -){
   852         -  Fts5ExprNearset *pNear = pNode->pNear;
   853         -  Fts5ExprPhrase *pLeft = pNear->apPhrase[0];
   854         -  int rc = SQLITE_OK;
   855         -  i64 iLast;                      /* Lastest rowid any iterator points to */
   856         -  int i, j;                       /* Phrase and token index, respectively */
   857         -  int bMatch;                     /* True if all terms are at the same rowid */
   858         -  const int bDesc = pExpr->bDesc;
   859         -
   860         -  /* Check that this node should not be FTS5_TERM */
   861         -  assert( pNear->nPhrase>1 
   862         -       || pNear->apPhrase[0]->nTerm>1 
   863         -       || pNear->apPhrase[0]->aTerm[0].pSynonym
   864         -  );
   865         -
   866         -  /* Initialize iLast, the "lastest" rowid any iterator points to. If the
   867         -  ** iterator skips through rowids in the default ascending order, this means
   868         -  ** the maximum rowid. Or, if the iterator is "ORDER BY rowid DESC", then it
   869         -  ** means the minimum rowid.  */
   870         -  if( pLeft->aTerm[0].pSynonym ){
   871         -    iLast = fts5ExprSynonymRowid(&pLeft->aTerm[0], bDesc, 0);
   872         -  }else{
   873         -    iLast = sqlite3Fts5IterRowid(pLeft->aTerm[0].pIter);
   874         -  }
   875         -
   876         -  do {
   877         -    bMatch = 1;
   878         -    for(i=0; i<pNear->nPhrase; i++){
   879         -      Fts5ExprPhrase *pPhrase = pNear->apPhrase[i];
   880         -      for(j=0; j<pPhrase->nTerm; j++){
   881         -        Fts5ExprTerm *pTerm = &pPhrase->aTerm[j];
   882         -        if( pTerm->pSynonym ){
   883         -          i64 iRowid = fts5ExprSynonymRowid(pTerm, bDesc, 0);
   884         -          if( iRowid==iLast ) continue;
   885         -          bMatch = 0;
   886         -          if( fts5ExprSynonymAdvanceto(pTerm, bDesc, &iLast, &rc) ){
   887         -            pNode->bNomatch = 0;
   888         -            pNode->bEof = 1;
   889         -            return rc;
   890         -          }
   891         -        }else{
   892         -          Fts5IndexIter *pIter = pPhrase->aTerm[j].pIter;
   893         -          i64 iRowid = sqlite3Fts5IterRowid(pIter);
   894         -          if( iRowid==iLast ) continue;
   895         -          bMatch = 0;
   896         -          if( fts5ExprAdvanceto(pIter, bDesc, &iLast, &rc, &pNode->bEof) ){
   897         -            return rc;
   898         -          }
   899         -        }
   900         -      }
   901         -    }
   902         -  }while( bMatch==0 );
   903         -
   904         -  pNode->iRowid = iLast;
   905         -  pNode->bNomatch = ((0==fts5ExprNearTest(&rc, pExpr, pNode)) && rc==SQLITE_OK);
   906         -  assert( pNode->bEof==0 || pNode->bNomatch==0 );
   907         -
   908         -  return rc;
   909         -}
   910    746   
   911    747   /*
   912    748   ** Initialize all term iterators in the pNear object. If any term is found
   913    749   ** to match no documents at all, return immediately without initializing any
   914    750   ** further iterators.
   915    751   */
   916    752   static int fts5ExprNearInitAll(
................................................................................
   952    788           return rc;
   953    789         }
   954    790       }
   955    791     }
   956    792   
   957    793     return rc;
   958    794   }
   959         -
   960         -/* fts5ExprNodeNext() calls fts5ExprNodeNextMatch(). And vice-versa. */
   961         -static int fts5ExprNodeNextMatch(Fts5Expr*, Fts5ExprNode*);
   962         -
   963    795   
   964    796   /*
   965    797   ** If pExpr is an ASC iterator, this function returns a value with the
   966    798   ** same sign as:
   967    799   **
   968    800   **   (iLhs - iRhs)
   969    801   **
................................................................................
  1008    840       for(i=0; i<pNode->nChild; i++){
  1009    841         fts5ExprNodeZeroPoslist(pNode->apChild[i]);
  1010    842       }
  1011    843     }
  1012    844   }
  1013    845   
  1014    846   
          847  +
          848  +/*
          849  +** Compare the values currently indicated by the two nodes as follows:
          850  +**
          851  +**    res = (*p1) - (*p2)
          852  +**
          853  +** Nodes that point to values that come later in the iteration order are
          854  +** considered to be larger. Nodes at EOF are the largest of all.
          855  +**
          856  +** This means that if the iteration order is ASC, then numerically larger
          857  +** rowids are considered larger. Or if it is the default DESC, numerically
          858  +** smaller rowids are larger.
          859  +*/
          860  +static int fts5NodeCompare(
          861  +  Fts5Expr *pExpr,
          862  +  Fts5ExprNode *p1, 
          863  +  Fts5ExprNode *p2
          864  +){
          865  +  if( p2->bEof ) return -1;
          866  +  if( p1->bEof ) return +1;
          867  +  return fts5RowidCmp(pExpr, p1->iRowid, p2->iRowid);
          868  +}
          869  +
          870  +/*
          871  +** All individual term iterators in pNear are guaranteed to be valid when
          872  +** this function is called. This function checks if all term iterators
          873  +** point to the same rowid, and if not, advances them until they do.
          874  +** If an EOF is reached before this happens, *pbEof is set to true before
          875  +** returning.
          876  +**
          877  +** SQLITE_OK is returned if an error occurs, or an SQLite error code 
          878  +** otherwise. It is not considered an error code if an iterator reaches
          879  +** EOF.
          880  +*/
          881  +static int fts5ExprNodeTest_STRING(
          882  +  Fts5Expr *pExpr,                /* Expression pPhrase belongs to */
          883  +  Fts5ExprNode *pNode
          884  +){
          885  +  Fts5ExprNearset *pNear = pNode->pNear;
          886  +  Fts5ExprPhrase *pLeft = pNear->apPhrase[0];
          887  +  int rc = SQLITE_OK;
          888  +  i64 iLast;                      /* Lastest rowid any iterator points to */
          889  +  int i, j;                       /* Phrase and token index, respectively */
          890  +  int bMatch;                     /* True if all terms are at the same rowid */
          891  +  const int bDesc = pExpr->bDesc;
          892  +
          893  +  /* Check that this node should not be FTS5_TERM */
          894  +  assert( pNear->nPhrase>1 
          895  +       || pNear->apPhrase[0]->nTerm>1 
          896  +       || pNear->apPhrase[0]->aTerm[0].pSynonym
          897  +  );
          898  +
          899  +  /* Initialize iLast, the "lastest" rowid any iterator points to. If the
          900  +  ** iterator skips through rowids in the default ascending order, this means
          901  +  ** the maximum rowid. Or, if the iterator is "ORDER BY rowid DESC", then it
          902  +  ** means the minimum rowid.  */
          903  +  if( pLeft->aTerm[0].pSynonym ){
          904  +    iLast = fts5ExprSynonymRowid(&pLeft->aTerm[0], bDesc, 0);
          905  +  }else{
          906  +    iLast = pLeft->aTerm[0].pIter->iRowid;
          907  +  }
          908  +
          909  +  do {
          910  +    bMatch = 1;
          911  +    for(i=0; i<pNear->nPhrase; i++){
          912  +      Fts5ExprPhrase *pPhrase = pNear->apPhrase[i];
          913  +      for(j=0; j<pPhrase->nTerm; j++){
          914  +        Fts5ExprTerm *pTerm = &pPhrase->aTerm[j];
          915  +        if( pTerm->pSynonym ){
          916  +          i64 iRowid = fts5ExprSynonymRowid(pTerm, bDesc, 0);
          917  +          if( iRowid==iLast ) continue;
          918  +          bMatch = 0;
          919  +          if( fts5ExprSynonymAdvanceto(pTerm, bDesc, &iLast, &rc) ){
          920  +            pNode->bNomatch = 0;
          921  +            pNode->bEof = 1;
          922  +            return rc;
          923  +          }
          924  +        }else{
          925  +          Fts5IndexIter *pIter = pPhrase->aTerm[j].pIter;
          926  +          if( pIter->iRowid==iLast ) continue;
          927  +          bMatch = 0;
          928  +          if( fts5ExprAdvanceto(pIter, bDesc, &iLast, &rc, &pNode->bEof) ){
          929  +            return rc;
          930  +          }
          931  +        }
          932  +      }
          933  +    }
          934  +  }while( bMatch==0 );
          935  +
          936  +  pNode->iRowid = iLast;
          937  +  pNode->bNomatch = ((0==fts5ExprNearTest(&rc, pExpr, pNode)) && rc==SQLITE_OK);
          938  +  assert( pNode->bEof==0 || pNode->bNomatch==0 );
          939  +
          940  +  return rc;
          941  +}
          942  +
          943  +/*
          944  +** Advance the first term iterator in the first phrase of pNear. Set output
          945  +** variable *pbEof to true if it reaches EOF or if an error occurs.
          946  +**
          947  +** Return SQLITE_OK if successful, or an SQLite error code if an error
          948  +** occurs.
          949  +*/
          950  +static int fts5ExprNodeNext_STRING(
          951  +  Fts5Expr *pExpr,                /* Expression pPhrase belongs to */
          952  +  Fts5ExprNode *pNode,            /* FTS5_STRING or FTS5_TERM node */
          953  +  int bFromValid,
          954  +  i64 iFrom 
          955  +){
          956  +  Fts5ExprTerm *pTerm = &pNode->pNear->apPhrase[0]->aTerm[0];
          957  +  int rc = SQLITE_OK;
          958  +
          959  +  pNode->bNomatch = 0;
          960  +  if( pTerm->pSynonym ){
          961  +    int bEof = 1;
          962  +    Fts5ExprTerm *p;
          963  +
          964  +    /* Find the firstest rowid any synonym points to. */
          965  +    i64 iRowid = fts5ExprSynonymRowid(pTerm, pExpr->bDesc, 0);
          966  +
          967  +    /* Advance each iterator that currently points to iRowid. Or, if iFrom
          968  +    ** is valid - each iterator that points to a rowid before iFrom.  */
          969  +    for(p=pTerm; p; p=p->pSynonym){
          970  +      if( sqlite3Fts5IterEof(p->pIter)==0 ){
          971  +        i64 ii = p->pIter->iRowid;
          972  +        if( ii==iRowid 
          973  +         || (bFromValid && ii!=iFrom && (ii>iFrom)==pExpr->bDesc) 
          974  +        ){
          975  +          if( bFromValid ){
          976  +            rc = sqlite3Fts5IterNextFrom(p->pIter, iFrom);
          977  +          }else{
          978  +            rc = sqlite3Fts5IterNext(p->pIter);
          979  +          }
          980  +          if( rc!=SQLITE_OK ) break;
          981  +          if( sqlite3Fts5IterEof(p->pIter)==0 ){
          982  +            bEof = 0;
          983  +          }
          984  +        }else{
          985  +          bEof = 0;
          986  +        }
          987  +      }
          988  +    }
          989  +
          990  +    /* Set the EOF flag if either all synonym iterators are at EOF or an
          991  +    ** error has occurred.  */
          992  +    pNode->bEof = (rc || bEof);
          993  +  }else{
          994  +    Fts5IndexIter *pIter = pTerm->pIter;
          995  +
          996  +    assert( Fts5NodeIsString(pNode) );
          997  +    if( bFromValid ){
          998  +      rc = sqlite3Fts5IterNextFrom(pIter, iFrom);
          999  +    }else{
         1000  +      rc = sqlite3Fts5IterNext(pIter);
         1001  +    }
         1002  +
         1003  +    pNode->bEof = (rc || sqlite3Fts5IterEof(pIter));
         1004  +  }
         1005  +
         1006  +  if( pNode->bEof==0 ){
         1007  +    assert( rc==SQLITE_OK );
         1008  +    rc = fts5ExprNodeTest_STRING(pExpr, pNode);
         1009  +  }
         1010  +
         1011  +  return rc;
         1012  +}
         1013  +
         1014  +
         1015  +static int fts5ExprNodeTest_TERM(
         1016  +  Fts5Expr *pExpr,                /* Expression that pNear is a part of */
         1017  +  Fts5ExprNode *pNode             /* The "NEAR" node (FTS5_TERM) */
         1018  +){
         1019  +  /* As this "NEAR" object is actually a single phrase that consists 
         1020  +  ** of a single term only, grab pointers into the poslist managed by the
         1021  +  ** fts5_index.c iterator object. This is much faster than synthesizing 
         1022  +  ** a new poslist the way we have to for more complicated phrase or NEAR
         1023  +  ** expressions.  */
         1024  +  Fts5ExprPhrase *pPhrase = pNode->pNear->apPhrase[0];
         1025  +  Fts5IndexIter *pIter = pPhrase->aTerm[0].pIter;
         1026  +
         1027  +  assert( pNode->eType==FTS5_TERM );
         1028  +  assert( pNode->pNear->nPhrase==1 && pPhrase->nTerm==1 );
         1029  +  assert( pPhrase->aTerm[0].pSynonym==0 );
         1030  +
         1031  +  pPhrase->poslist.n = pIter->nData;
         1032  +  if( pExpr->pConfig->eDetail==FTS5_DETAIL_FULL ){
         1033  +    pPhrase->poslist.p = (u8*)pIter->pData;
         1034  +  }
         1035  +  pNode->iRowid = pIter->iRowid;
         1036  +  pNode->bNomatch = (pPhrase->poslist.n==0);
         1037  +  return SQLITE_OK;
         1038  +}
         1039  +
         1040  +/*
         1041  +** xNext() method for a node of type FTS5_TERM.
         1042  +*/
         1043  +static int fts5ExprNodeNext_TERM(
         1044  +  Fts5Expr *pExpr, 
         1045  +  Fts5ExprNode *pNode,
         1046  +  int bFromValid,
         1047  +  i64 iFrom
         1048  +){
         1049  +  int rc;
         1050  +  Fts5IndexIter *pIter = pNode->pNear->apPhrase[0]->aTerm[0].pIter;
         1051  +
         1052  +  assert( pNode->bEof==0 );
         1053  +  if( bFromValid ){
         1054  +    rc = sqlite3Fts5IterNextFrom(pIter, iFrom);
         1055  +  }else{
         1056  +    rc = sqlite3Fts5IterNext(pIter);
         1057  +  }
         1058  +  if( rc==SQLITE_OK && sqlite3Fts5IterEof(pIter)==0 ){
         1059  +    rc = fts5ExprNodeTest_TERM(pExpr, pNode);
         1060  +  }else{
         1061  +    pNode->bEof = 1;
         1062  +    pNode->bNomatch = 0;
         1063  +  }
         1064  +  return rc;
         1065  +}
         1066  +
         1067  +static void fts5ExprNodeTest_OR(
         1068  +  Fts5Expr *pExpr,                /* Expression of which pNode is a part */
         1069  +  Fts5ExprNode *pNode             /* Expression node to test */
         1070  +){
         1071  +  Fts5ExprNode *pNext = pNode->apChild[0];
         1072  +  int i;
         1073  +
         1074  +  for(i=1; i<pNode->nChild; i++){
         1075  +    Fts5ExprNode *pChild = pNode->apChild[i];
         1076  +    int cmp = fts5NodeCompare(pExpr, pNext, pChild);
         1077  +    if( cmp>0 || (cmp==0 && pChild->bNomatch==0) ){
         1078  +      pNext = pChild;
         1079  +    }
         1080  +  }
         1081  +  pNode->iRowid = pNext->iRowid;
         1082  +  pNode->bEof = pNext->bEof;
         1083  +  pNode->bNomatch = pNext->bNomatch;
         1084  +}
         1085  +
         1086  +static int fts5ExprNodeNext_OR(
         1087  +  Fts5Expr *pExpr, 
         1088  +  Fts5ExprNode *pNode,
         1089  +  int bFromValid,
         1090  +  i64 iFrom
         1091  +){
         1092  +  int i;
         1093  +  i64 iLast = pNode->iRowid;
         1094  +
         1095  +  for(i=0; i<pNode->nChild; i++){
         1096  +    Fts5ExprNode *p1 = pNode->apChild[i];
         1097  +    assert( p1->bEof || fts5RowidCmp(pExpr, p1->iRowid, iLast)>=0 );
         1098  +    if( p1->bEof==0 ){
         1099  +      if( (p1->iRowid==iLast) 
         1100  +       || (bFromValid && fts5RowidCmp(pExpr, p1->iRowid, iFrom)<0)
         1101  +      ){
         1102  +        int rc = fts5ExprNodeNext(pExpr, p1, bFromValid, iFrom);
         1103  +        if( rc!=SQLITE_OK ) return rc;
         1104  +      }
         1105  +    }
         1106  +  }
         1107  +
         1108  +  fts5ExprNodeTest_OR(pExpr, pNode);
         1109  +  return SQLITE_OK;
         1110  +}
         1111  +
  1015   1112   /*
  1016   1113   ** Argument pNode is an FTS5_AND node.
  1017   1114   */
  1018         -static int fts5ExprAndNextRowid(
         1115  +static int fts5ExprNodeTest_AND(
  1019   1116     Fts5Expr *pExpr,                /* Expression pPhrase belongs to */
  1020   1117     Fts5ExprNode *pAnd              /* FTS5_AND node to advance */
  1021   1118   ){
  1022   1119     int iChild;
  1023   1120     i64 iLast = pAnd->iRowid;
  1024   1121     int rc = SQLITE_OK;
  1025   1122     int bMatch;
................................................................................
  1026   1123   
  1027   1124     assert( pAnd->bEof==0 );
  1028   1125     do {
  1029   1126       pAnd->bNomatch = 0;
  1030   1127       bMatch = 1;
  1031   1128       for(iChild=0; iChild<pAnd->nChild; iChild++){
  1032   1129         Fts5ExprNode *pChild = pAnd->apChild[iChild];
  1033         -      if( 0 && pChild->eType==FTS5_STRING ){
  1034         -        /* TODO */
  1035         -      }else{
  1036         -        int cmp = fts5RowidCmp(pExpr, iLast, pChild->iRowid);
  1037         -        if( cmp>0 ){
  1038         -          /* Advance pChild until it points to iLast or laster */
  1039         -          rc = fts5ExprNodeNext(pExpr, pChild, 1, iLast);
  1040         -          if( rc!=SQLITE_OK ) return rc;
  1041         -        }
         1130  +      int cmp = fts5RowidCmp(pExpr, iLast, pChild->iRowid);
         1131  +      if( cmp>0 ){
         1132  +        /* Advance pChild until it points to iLast or laster */
         1133  +        rc = fts5ExprNodeNext(pExpr, pChild, 1, iLast);
         1134  +        if( rc!=SQLITE_OK ) return rc;
  1042   1135         }
  1043   1136   
  1044   1137         /* If the child node is now at EOF, so is the parent AND node. Otherwise,
  1045   1138         ** the child node is guaranteed to have advanced at least as far as
  1046   1139         ** rowid iLast. So if it is not at exactly iLast, pChild->iRowid is the
  1047   1140         ** new lastest rowid seen so far.  */
  1048   1141         assert( pChild->bEof || fts5RowidCmp(pExpr, iLast, pChild->iRowid)<=0 );
................................................................................
  1064   1157     if( pAnd->bNomatch && pAnd!=pExpr->pRoot ){
  1065   1158       fts5ExprNodeZeroPoslist(pAnd);
  1066   1159     }
  1067   1160     pAnd->iRowid = iLast;
  1068   1161     return SQLITE_OK;
  1069   1162   }
  1070   1163   
  1071         -
  1072         -/*
  1073         -** Compare the values currently indicated by the two nodes as follows:
  1074         -**
  1075         -**    res = (*p1) - (*p2)
  1076         -**
  1077         -** Nodes that point to values that come later in the iteration order are
  1078         -** considered to be larger. Nodes at EOF are the largest of all.
  1079         -**
  1080         -** This means that if the iteration order is ASC, then numerically larger
  1081         -** rowids are considered larger. Or if it is the default DESC, numerically
  1082         -** smaller rowids are larger.
  1083         -*/
  1084         -static int fts5NodeCompare(
  1085         -  Fts5Expr *pExpr,
  1086         -  Fts5ExprNode *p1, 
  1087         -  Fts5ExprNode *p2
  1088         -){
  1089         -  if( p2->bEof ) return -1;
  1090         -  if( p1->bEof ) return +1;
  1091         -  return fts5RowidCmp(pExpr, p1->iRowid, p2->iRowid);
  1092         -}
  1093         -
  1094         -/*
  1095         -** xNext() method for a node of type FTS5_TERM.
  1096         -*/
  1097         -static int fts5ExprNodeNext_Term(
         1164  +static int fts5ExprNodeNext_AND(
  1098   1165     Fts5Expr *pExpr, 
  1099   1166     Fts5ExprNode *pNode,
  1100   1167     int bFromValid,
  1101   1168     i64 iFrom
  1102   1169   ){
  1103         -  int rc;
  1104         -  Fts5IndexIter *pIter = pNode->pNear->apPhrase[0]->aTerm[0].pIter;
  1105         -
  1106         -  assert( pNode->bEof==0 );
  1107         -  if( bFromValid ){
  1108         -    rc = sqlite3Fts5IterNextFrom(pIter, iFrom);
  1109         -  }else{
  1110         -    rc = sqlite3Fts5IterNext(pIter);
  1111         -  }
  1112         -  if( rc==SQLITE_OK && sqlite3Fts5IterEof(pIter)==0 ){
  1113         -    rc = fts5ExprTokenTest(pExpr, pNode);
  1114         -  }else{
  1115         -    pNode->bEof = 1;
  1116         -    pNode->bNomatch = 0;
  1117         -  }
  1118         -  return rc;
  1119         -}
  1120         -
  1121         -/*
  1122         -** Advance node iterator pNode, part of expression pExpr. If argument
  1123         -** bFromValid is zero, then pNode is advanced exactly once. Or, if argument
  1124         -** bFromValid is non-zero, then pNode is advanced until it is at or past
  1125         -** rowid value iFrom. Whether "past" means "less than" or "greater than"
  1126         -** depends on whether this is an ASC or DESC iterator.
  1127         -*/
  1128         -static int fts5ExprNodeNext_Fallback(
         1170  +  int rc = fts5ExprNodeNext(pExpr, pNode->apChild[0], bFromValid, iFrom);
         1171  +  if( rc==SQLITE_OK ){
         1172  +    rc = fts5ExprNodeTest_AND(pExpr, pNode);
         1173  +  }
         1174  +  return rc;
         1175  +}
         1176  +
         1177  +static int fts5ExprNodeTest_NOT(
         1178  +  Fts5Expr *pExpr,                /* Expression pPhrase belongs to */
         1179  +  Fts5ExprNode *pNode             /* FTS5_NOT node to advance */
         1180  +){
         1181  +  int rc = SQLITE_OK;
         1182  +  Fts5ExprNode *p1 = pNode->apChild[0];
         1183  +  Fts5ExprNode *p2 = pNode->apChild[1];
         1184  +  assert( pNode->nChild==2 );
         1185  +
         1186  +  while( rc==SQLITE_OK && p1->bEof==0 ){
         1187  +    int cmp = fts5NodeCompare(pExpr, p1, p2);
         1188  +    if( cmp>0 ){
         1189  +      rc = fts5ExprNodeNext(pExpr, p2, 1, p1->iRowid);
         1190  +      cmp = fts5NodeCompare(pExpr, p1, p2);
         1191  +    }
         1192  +    assert( rc!=SQLITE_OK || cmp<=0 );
         1193  +    if( cmp || p2->bNomatch ) break;
         1194  +    rc = fts5ExprNodeNext(pExpr, p1, 0, 0);
         1195  +  }
         1196  +  pNode->bEof = p1->bEof;
         1197  +  pNode->bNomatch = p1->bNomatch;
         1198  +  pNode->iRowid = p1->iRowid;
         1199  +  if( p1->bEof ){
         1200  +    fts5ExprNodeZeroPoslist(p2);
         1201  +  }
         1202  +  return rc;
         1203  +}
         1204  +
         1205  +static int fts5ExprNodeNext_NOT(
  1129   1206     Fts5Expr *pExpr, 
  1130   1207     Fts5ExprNode *pNode,
  1131   1208     int bFromValid,
  1132   1209     i64 iFrom
  1133   1210   ){
  1134         -  int rc = SQLITE_OK;
  1135         -
  1136         -  if( pNode->bEof==0 ){
  1137         -    switch( pNode->eType ){
  1138         -      case FTS5_STRING: {
  1139         -        rc = fts5ExprNearAdvanceFirst(pExpr, pNode, bFromValid, iFrom);
  1140         -        break;
  1141         -      };
  1142         -
  1143         -      case FTS5_TERM: {
  1144         -        Fts5IndexIter *pIter = pNode->pNear->apPhrase[0]->aTerm[0].pIter;
  1145         -        if( bFromValid ){
  1146         -          rc = sqlite3Fts5IterNextFrom(pIter, iFrom);
  1147         -        }else{
  1148         -          rc = sqlite3Fts5IterNext(pIter);
  1149         -        }
  1150         -        if( rc==SQLITE_OK && sqlite3Fts5IterEof(pIter)==0 ){
  1151         -          assert( rc==SQLITE_OK );
  1152         -          rc = fts5ExprTokenTest(pExpr, pNode);
  1153         -        }else{
  1154         -          pNode->bEof = 1;
  1155         -          pNode->bNomatch = 0;
  1156         -        }
  1157         -        return rc;
  1158         -      };
  1159         -
  1160         -      case FTS5_AND: {
  1161         -        Fts5ExprNode *pLeft = pNode->apChild[0];
  1162         -        rc = fts5ExprNodeNext(pExpr, pLeft, bFromValid, iFrom);
  1163         -        break;
  1164         -      }
  1165         -
  1166         -      case FTS5_OR: {
  1167         -        int i;
  1168         -        i64 iLast = pNode->iRowid;
  1169         -
  1170         -        for(i=0; rc==SQLITE_OK && i<pNode->nChild; i++){
  1171         -          Fts5ExprNode *p1 = pNode->apChild[i];
  1172         -          assert( p1->bEof || fts5RowidCmp(pExpr, p1->iRowid, iLast)>=0 );
  1173         -          if( p1->bEof==0 ){
  1174         -            if( (p1->iRowid==iLast) 
  1175         -             || (bFromValid && fts5RowidCmp(pExpr, p1->iRowid, iFrom)<0)
  1176         -            ){
  1177         -              rc = fts5ExprNodeNext(pExpr, p1, bFromValid, iFrom);
  1178         -            }
  1179         -          }
  1180         -        }
  1181         -
  1182         -        break;
  1183         -      }
  1184         -
  1185         -      default: assert( pNode->eType==FTS5_NOT ); {
  1186         -        assert( pNode->nChild==2 );
  1187         -        rc = fts5ExprNodeNext(pExpr, pNode->apChild[0], bFromValid, iFrom);
  1188         -        break;
  1189         -      }
  1190         -    }
  1191         -
  1192         -    if( rc==SQLITE_OK ){
  1193         -      rc = fts5ExprNodeNextMatch(pExpr, pNode);
  1194         -    }
  1195         -  }
  1196         -
  1197         -  /* Assert that if bFromValid was true, either:
  1198         -  **
  1199         -  **   a) an error occurred, or
  1200         -  **   b) the node is now at EOF, or
  1201         -  **   c) the node is now at or past rowid iFrom.
  1202         -  */
  1203         -  assert( bFromValid==0 
  1204         -      || rc!=SQLITE_OK                                                  /* a */
  1205         -      || pNode->bEof                                                    /* b */
  1206         -      || pNode->iRowid==iFrom || pExpr->bDesc==(pNode->iRowid<iFrom)    /* c */
  1207         -  );
  1208         -
  1209         -  assert( pNode->bNomatch==0 || rc==SQLITE_OK );
  1210         -  return rc;
  1211         -}
  1212         -
         1211  +  int rc = fts5ExprNodeNext(pExpr, pNode->apChild[0], bFromValid, iFrom);
         1212  +  if( rc==SQLITE_OK ){
         1213  +    rc = fts5ExprNodeTest_NOT(pExpr, pNode);
         1214  +  }
         1215  +  return rc;
         1216  +}
  1213   1217   
  1214   1218   /*
  1215   1219   ** If pNode currently points to a match, this function returns SQLITE_OK
  1216   1220   ** without modifying it. Otherwise, pNode is advanced until it does point
  1217   1221   ** to a match or EOF is reached.
  1218   1222   */
  1219         -static int fts5ExprNodeNextMatch(
         1223  +static int fts5ExprNodeTest(
  1220   1224     Fts5Expr *pExpr,                /* Expression of which pNode is a part */
  1221   1225     Fts5ExprNode *pNode             /* Expression node to test */
  1222   1226   ){
  1223   1227     int rc = SQLITE_OK;
  1224   1228     if( pNode->bEof==0 ){
  1225   1229       switch( pNode->eType ){
  1226   1230   
  1227   1231         case FTS5_STRING: {
  1228         -        /* Advance the iterators until they all point to the same rowid */
  1229         -        rc = fts5ExprNearNextMatch(pExpr, pNode);
         1232  +        rc = fts5ExprNodeTest_STRING(pExpr, pNode);
  1230   1233           break;
  1231   1234         }
  1232   1235   
  1233   1236         case FTS5_TERM: {
  1234         -        rc = fts5ExprTokenTest(pExpr, pNode);
         1237  +        rc = fts5ExprNodeTest_TERM(pExpr, pNode);
  1235   1238           break;
  1236   1239         }
  1237   1240   
  1238   1241         case FTS5_AND: {
  1239         -        rc = fts5ExprAndNextRowid(pExpr, pNode);
         1242  +        rc = fts5ExprNodeTest_AND(pExpr, pNode);
  1240   1243           break;
  1241   1244         }
  1242   1245   
  1243   1246         case FTS5_OR: {
  1244         -        Fts5ExprNode *pNext = pNode->apChild[0];
  1245         -        int i;
  1246         -
  1247         -        for(i=1; i<pNode->nChild; i++){
  1248         -          Fts5ExprNode *pChild = pNode->apChild[i];
  1249         -          int cmp = fts5NodeCompare(pExpr, pNext, pChild);
  1250         -          if( cmp>0 || (cmp==0 && pChild->bNomatch==0) ){
  1251         -            pNext = pChild;
  1252         -          }
  1253         -        }
  1254         -        pNode->iRowid = pNext->iRowid;
  1255         -        pNode->bEof = pNext->bEof;
  1256         -        pNode->bNomatch = pNext->bNomatch;
         1247  +        fts5ExprNodeTest_OR(pExpr, pNode);
  1257   1248           break;
  1258   1249         }
  1259   1250   
  1260   1251         default: assert( pNode->eType==FTS5_NOT ); {
  1261         -        Fts5ExprNode *p1 = pNode->apChild[0];
  1262         -        Fts5ExprNode *p2 = pNode->apChild[1];
  1263         -        assert( pNode->nChild==2 );
  1264         -
  1265         -        while( rc==SQLITE_OK && p1->bEof==0 ){
  1266         -          int cmp = fts5NodeCompare(pExpr, p1, p2);
  1267         -          if( cmp>0 ){
  1268         -            rc = fts5ExprNodeNext(pExpr, p2, 1, p1->iRowid);
  1269         -            cmp = fts5NodeCompare(pExpr, p1, p2);
  1270         -          }
  1271         -          assert( rc!=SQLITE_OK || cmp<=0 );
  1272         -          if( cmp || p2->bNomatch ) break;
  1273         -          rc = fts5ExprNodeNext(pExpr, p1, 0, 0);
  1274         -        }
  1275         -        pNode->bEof = p1->bEof;
  1276         -        pNode->bNomatch = p1->bNomatch;
  1277         -        pNode->iRowid = p1->iRowid;
  1278         -        if( p1->bEof ){
  1279         -          fts5ExprNodeZeroPoslist(p2);
  1280         -        }
         1252  +        rc = fts5ExprNodeTest_NOT(pExpr, pNode);
  1281   1253           break;
  1282   1254         }
  1283   1255       }
  1284   1256     }
  1285   1257     return rc;
  1286   1258   }
  1287   1259   
................................................................................
  1325   1297           assert( pNode->eType==FTS5_NOT );
  1326   1298           pNode->bEof = pNode->apChild[0]->bEof;
  1327   1299           break;
  1328   1300       }
  1329   1301     }
  1330   1302   
  1331   1303     if( rc==SQLITE_OK ){
  1332         -    rc = fts5ExprNodeNextMatch(pExpr, pNode);
         1304  +    rc = fts5ExprNodeTest(pExpr, pNode);
  1333   1305     }
  1334   1306     return rc;
  1335   1307   }
  1336   1308   
  1337   1309   
  1338   1310   /*
  1339   1311   ** Begin iterating through the set of documents in index pIdx matched by
................................................................................
  1686   1658       /* All the allocations succeeded. Put the expression object together. */
  1687   1659       pNew->pIndex = pExpr->pIndex;
  1688   1660       pNew->pConfig = pExpr->pConfig;
  1689   1661       pNew->nPhrase = 1;
  1690   1662       pNew->apExprPhrase[0] = sCtx.pPhrase;
  1691   1663       pNew->pRoot->pNear->apPhrase[0] = sCtx.pPhrase;
  1692   1664       pNew->pRoot->pNear->nPhrase = 1;
  1693         -    pNew->pRoot->xNext = fts5ExprNodeNext_Fallback;
  1694   1665       sCtx.pPhrase->pNode = pNew->pRoot;
  1695   1666   
  1696   1667       if( pOrig->nTerm==1 && pOrig->aTerm[0].pSynonym==0 ){
  1697   1668         pNew->pRoot->eType = FTS5_TERM;
         1669  +      pNew->pRoot->xNext = fts5ExprNodeNext_TERM;
  1698   1670       }else{
  1699   1671         pNew->pRoot->eType = FTS5_STRING;
         1672  +      pNew->pRoot->xNext = fts5ExprNodeNext_STRING;
  1700   1673       }
  1701   1674     }else{
  1702   1675       sqlite3Fts5ExprFree(pNew);
  1703   1676       fts5ExprPhraseFree(sCtx.pPhrase);
  1704   1677       pNew = 0;
  1705   1678     }
  1706   1679   
................................................................................
  1839   1812   
  1840   1813     if( pNear ){
  1841   1814       pNear->pColset = pColset;
  1842   1815     }else{
  1843   1816       sqlite3_free(pColset);
  1844   1817     }
  1845   1818   }
         1819  +
         1820  +static void fts5ExprAssignXNext(Fts5ExprNode *pNode){
         1821  +  switch( pNode->eType ){
         1822  +    case FTS5_STRING: {
         1823  +      Fts5ExprNearset *pNear = pNode->pNear;
         1824  +      if( pNear->nPhrase==1 && pNear->apPhrase[0]->nTerm==1 
         1825  +       && pNear->apPhrase[0]->aTerm[0].pSynonym==0
         1826  +      ){
         1827  +        pNode->eType = FTS5_TERM;
         1828  +        pNode->xNext = fts5ExprNodeNext_TERM;
         1829  +      }else{
         1830  +        pNode->xNext = fts5ExprNodeNext_STRING;
         1831  +      }
         1832  +      break;
         1833  +    };
         1834  +
         1835  +    case FTS5_OR: {
         1836  +      pNode->xNext = fts5ExprNodeNext_OR;
         1837  +      break;
         1838  +    };
         1839  +
         1840  +    case FTS5_AND: {
         1841  +      pNode->xNext = fts5ExprNodeNext_AND;
         1842  +      break;
         1843  +    };
         1844  +
         1845  +    default: assert( pNode->eType==FTS5_NOT ); {
         1846  +      pNode->xNext = fts5ExprNodeNext_NOT;
         1847  +      break;
         1848  +    };
         1849  +  }
         1850  +}
  1846   1851   
  1847   1852   static void fts5ExprAddChildren(Fts5ExprNode *p, Fts5ExprNode *pSub){
  1848   1853     if( p->eType!=FTS5_NOT && pSub->eType==p->eType ){
  1849   1854       int nByte = sizeof(Fts5ExprNode*) * pSub->nChild;
  1850   1855       memcpy(&p->apChild[p->nChild], pSub->apChild, nByte);
  1851   1856       p->nChild += pSub->nChild;
  1852   1857       sqlite3_free(pSub);
................................................................................
  1887   1892         if( pRight->eType==eType ) nChild += pRight->nChild-1;
  1888   1893       }
  1889   1894   
  1890   1895       nByte = sizeof(Fts5ExprNode) + sizeof(Fts5ExprNode*)*(nChild-1);
  1891   1896       pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte);
  1892   1897   
  1893   1898       if( pRet ){
  1894         -      pRet->xNext = fts5ExprNodeNext_Fallback;
  1895   1899         pRet->eType = eType;
  1896   1900         pRet->pNear = pNear;
         1901  +      fts5ExprAssignXNext(pRet);
  1897   1902         if( eType==FTS5_STRING ){
  1898   1903           int iPhrase;
  1899   1904           for(iPhrase=0; iPhrase<pNear->nPhrase; iPhrase++){
  1900   1905             pNear->apPhrase[iPhrase]->pNode = pRet;
  1901   1906           }
  1902         -        if( pNear->nPhrase==1 && pNear->apPhrase[0]->nTerm==1 ){
  1903         -          if( pNear->apPhrase[0]->aTerm[0].pSynonym==0 ){
  1904         -            pRet->eType = FTS5_TERM;
  1905         -            pRet->xNext = fts5ExprNodeNext_Term;
  1906         -          }
  1907         -        }else if( pParse->pConfig->eDetail!=FTS5_DETAIL_FULL ){
         1907  +
         1908  +        if( pParse->pConfig->eDetail!=FTS5_DETAIL_FULL 
         1909  +         && (pNear->nPhrase!=1 || pNear->apPhrase[0]->nTerm!=1)
         1910  +        ){
  1908   1911             assert( pParse->rc==SQLITE_OK );
  1909   1912             pParse->rc = SQLITE_ERROR;
  1910   1913             assert( pParse->zErr==0 );
  1911   1914             pParse->zErr = sqlite3_mprintf(
  1912   1915                 "fts5: %s queries are not supported (detail!=full)", 
  1913   1916                 pNear->nPhrase==1 ? "phrase": "NEAR"
  1914   1917             );
  1915   1918             sqlite3_free(pRet);
  1916   1919             pRet = 0;
  1917   1920           }
         1921  +
  1918   1922         }else{
  1919   1923           fts5ExprAddChildren(pRet, pLeft);
  1920   1924           fts5ExprAddChildren(pRet, pRight);
  1921   1925         }
  1922   1926       }
  1923   1927     }
  1924   1928   

Changes to ext/fts5/fts5_index.c.

   514    514   
   515    515     /* Invoked to set output variables. */
   516    516     void (*xSetOutputs)(Fts5Iter*, Fts5SegIter*);
   517    517   
   518    518     int nSeg;                       /* Size of aSeg[] array */
   519    519     int bRev;                       /* True to iterate in reverse order */
   520    520     u8 bSkipEmpty;                  /* True to skip deleted entries */
   521         -  u8 bEof;                        /* True at EOF */
   522    521     u8 bFiltered;                   /* True if column-filter already applied */
   523    522   
   524    523     i64 iSwitchRowid;               /* Firstest rowid of other than aFirst[1] */
   525    524     Fts5CResult *aFirst;            /* Current merge state (see above) */
   526    525     Fts5SegIter aSeg[1];            /* Array of segment iterators */
   527    526   };
   528    527   
................................................................................
  2455   2454   ** are correct.
  2456   2455   */
  2457   2456   static void fts5AssertMultiIterSetup(Fts5Index *p, Fts5Iter *pIter){
  2458   2457     if( p->rc==SQLITE_OK ){
  2459   2458       Fts5SegIter *pFirst = &pIter->aSeg[ pIter->aFirst[1].iFirst ];
  2460   2459       int i;
  2461   2460   
  2462         -    assert( (pFirst->pLeaf==0)==pIter->bEof );
         2461  +    assert( (pFirst->pLeaf==0)==pIter->base.bEof );
  2463   2462   
  2464   2463       /* Check that pIter->iSwitchRowid is set correctly. */
  2465   2464       for(i=0; i<pIter->nSeg; i++){
  2466   2465         Fts5SegIter *p1 = &pIter->aSeg[i];
  2467   2466         assert( p1==pFirst 
  2468   2467              || p1->pLeaf==0 
  2469   2468              || fts5BufferCompare(&pFirst->term, &p1->term) 
................................................................................
  2727   2726   }
  2728   2727   
  2729   2728   /*
  2730   2729   ** Set the pIter->bEof variable based on the state of the sub-iterators.
  2731   2730   */
  2732   2731   static void fts5MultiIterSetEof(Fts5Iter *pIter){
  2733   2732     Fts5SegIter *pSeg = &pIter->aSeg[ pIter->aFirst[1].iFirst ];
  2734         -  pIter->bEof = pSeg->pLeaf==0;
         2733  +  pIter->base.bEof = pSeg->pLeaf==0;
  2735   2734     pIter->iSwitchRowid = pSeg->iRowid;
  2736   2735   }
  2737   2736   
  2738   2737   /*
  2739   2738   ** Move the iterator to the next entry. 
  2740   2739   **
  2741   2740   ** If an error occurs, an error code is left in Fts5Index.rc. It is not 
................................................................................
  2960   2959           pIter->flags |= FTS5_SEGITER_REVERSE;
  2961   2960           fts5SegIterReverseInitPage(p, pIter);
  2962   2961         }else{
  2963   2962           fts5SegIterLoadNPos(p, pIter);
  2964   2963         }
  2965   2964         pData = 0;
  2966   2965       }else{
  2967         -      pNew->bEof = 1;
         2966  +      pNew->base.bEof = 1;
  2968   2967       }
  2969   2968       fts5SegIterSetNext(p, pIter);
  2970   2969   
  2971   2970       *ppOut = pNew;
  2972   2971     }
  2973   2972   
  2974   2973     fts5DataRelease(pData);
................................................................................
  2976   2975   
  2977   2976   /*
  2978   2977   ** Return true if the iterator is at EOF or if an error has occurred. 
  2979   2978   ** False otherwise.
  2980   2979   */
  2981   2980   static int fts5MultiIterEof(Fts5Index *p, Fts5Iter *pIter){
  2982   2981     assert( p->rc 
  2983         -      || (pIter->aSeg[ pIter->aFirst[1].iFirst ].pLeaf==0)==pIter->bEof 
         2982  +      || (pIter->aSeg[ pIter->aFirst[1].iFirst ].pLeaf==0)==pIter->base.bEof 
  2984   2983     );
  2985         -  return (p->rc || pIter->bEof);
         2984  +  return (p->rc || pIter->base.bEof);
  2986   2985   }
  2987   2986   
  2988   2987   /*
  2989   2988   ** Return the rowid of the entry that the iterator currently points
  2990   2989   ** to. If the iterator points to EOF when this function is called the
  2991   2990   ** results are undefined.
  2992   2991   */
................................................................................
  5205   5204     }
  5206   5205     return fts5IndexReturn(p);
  5207   5206   }
  5208   5207   
  5209   5208   /*
  5210   5209   ** Return true if the iterator passed as the only argument is at EOF.
  5211   5210   */
  5212         -int sqlite3Fts5IterEof(Fts5IndexIter *pIter){
  5213         -  assert( ((Fts5Iter*)pIter)->pIndex->rc==SQLITE_OK );
  5214         -  return ((Fts5Iter*)pIter)->bEof;
  5215         -}
  5216         -
  5217   5211   /*
  5218   5212   ** Move to the next matching rowid. 
  5219   5213   */
  5220   5214   int sqlite3Fts5IterNext(Fts5IndexIter *pIndexIter){
  5221   5215     Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
  5222   5216     assert( pIter->pIndex->rc==SQLITE_OK );
  5223   5217     fts5MultiIterNext(pIter->pIndex, pIter, 0, 0);
................................................................................
  5235   5229   
  5236   5230     fts5MultiIterNext(p, pIter, 0, 0);
  5237   5231     if( p->rc==SQLITE_OK ){
  5238   5232       Fts5SegIter *pSeg = &pIter->aSeg[ pIter->aFirst[1].iFirst ];
  5239   5233       if( pSeg->pLeaf && pSeg->term.p[0]!=FTS5_MAIN_PREFIX ){
  5240   5234         fts5DataRelease(pSeg->pLeaf);
  5241   5235         pSeg->pLeaf = 0;
  5242         -      pIter->bEof = 1;
         5236  +      pIter->base.bEof = 1;
  5243   5237       }
  5244   5238     }
  5245   5239   
  5246   5240     return fts5IndexReturn(pIter->pIndex);
  5247   5241   }
  5248   5242   
  5249   5243   /*
................................................................................
  5253   5247   */
  5254   5248   int sqlite3Fts5IterNextFrom(Fts5IndexIter *pIndexIter, i64 iMatch){
  5255   5249     Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
  5256   5250     fts5MultiIterNextFrom(pIter->pIndex, pIter, iMatch);
  5257   5251     return fts5IndexReturn(pIter->pIndex);
  5258   5252   }
  5259   5253   
  5260         -/*
  5261         -** Return the current rowid.
  5262         -*/
  5263         -i64 sqlite3Fts5IterRowid(Fts5IndexIter *pIndexIter){
  5264         -  return fts5MultiIterRowid((Fts5Iter*)pIndexIter);
  5265         -}
  5266         -
  5267   5254   /*
  5268   5255   ** Return the current term.
  5269   5256   */
  5270   5257   const char *sqlite3Fts5IterTerm(Fts5IndexIter *pIndexIter, int *pn){
  5271   5258     int n;
  5272   5259     const char *z = (const char*)fts5MultiIterTerm((Fts5Iter*)pIndexIter, &n);
  5273   5260     *pn = n-1;
................................................................................
  5446   5433   ){
  5447   5434     int eDetail = p->pConfig->eDetail;
  5448   5435     u64 cksum = *pCksum;
  5449   5436     Fts5IndexIter *pIter = 0;
  5450   5437     int rc = sqlite3Fts5IndexQuery(p, z, n, flags, 0, &pIter);
  5451   5438   
  5452   5439     while( rc==SQLITE_OK && 0==sqlite3Fts5IterEof(pIter) ){
  5453         -    i64 rowid = sqlite3Fts5IterRowid(pIter);
         5440  +    i64 rowid = pIter->iRowid;
  5454   5441   
  5455   5442       if( eDetail==FTS5_DETAIL_NONE ){
  5456   5443         cksum ^= sqlite3Fts5IndexEntryCksum(rowid, 0, 0, iIdx, z, n);
  5457   5444       }else{
  5458   5445         Fts5PoslistReader sReader;
  5459   5446         for(sqlite3Fts5PoslistReaderInit(pIter->pData, pIter->nData, &sReader);
  5460   5447             sReader.bEof==0;

Changes to ext/fts5/tool/fts5speed.tcl.

     8      8     {100 "SELECT count(*) FROM t1 WHERE t1 MATCH 'enron AND myapps'"}
     9      9     {1   "SELECT count(*) FROM t1 WHERE t1 MATCH 'en* AND my*'"}
    10     10   
    11     11     {1   "SELECT count(*) FROM t1 WHERE t1 MATCH 'c:t*'"}
    12     12     {1   "SELECT count(*) FROM t1 WHERE t1 MATCH 'a:t* OR b:t* OR c:t* OR d:t* OR e:t* OR f:t* OR g:t*'"}
    13     13     {1   "SELECT count(*) FROM t1 WHERE t1 MATCH 'a:t*'"}
    14     14     {2   "SELECT count(*) FROM t1 WHERE t1 MATCH 'c:the'"}
           15  +
           16  +  {2   "SELECT count(*) FROM t1 WHERE t1 MATCH 'd:holmes OR e:holmes OR f:holmes OR g:holmes'" }
           17  +  {2   "SELECT count(*) FROM t1 WHERE t1 MATCH 'd:holmes AND e:holmes AND f:holmes AND g:holmes'" }
           18  +  {4   "SELECT count(*) FROM t1 WHERE t1 MATCH 'd:holmes NOT e:holmes'" }
    15     19   }
    16     20   
    17     21   proc usage {} {
    18     22     global Q
    19     23     puts stderr "Usage: $::argv0 DATABASE QUERY"
    20     24     puts stderr ""
    21     25     for {set i 1} {$i <= [llength $Q]} {incr i} {