SQLite4
Check-in [a235305d42]
Not logged in

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

Overview
Comment:Add APIs to allow fts5 to be augmented with ranking and snippet functions. Does not work yet.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | matchinfo
Files: files | file ages | folders
SHA1: a235305d42ed0b17be6e18d4932d97046f9c03da
User & Date: dan 2013-01-01 19:56:04
Context
2013-01-02
20:01
Add an implementation of BM25 to fts5func.c. Other changes to matchinfo related things. check-in: 03f26d8c60 user: dan tags: matchinfo
2013-01-01
19:56
Add APIs to allow fts5 to be augmented with ranking and snippet functions. Does not work yet. check-in: a235305d42 user: dan tags: matchinfo
18:41
Fix a memory leak in fts5.c. check-in: 7bc0e58875 user: dan tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/expr.c.

  2586   2586             pColl = sqlite4ExprCollSeq(pParse, pFarg->a[i].pExpr);
  2587   2587           }
  2588   2588         }
  2589   2589         if( pDef->flags & SQLITE4_FUNC_NEEDCOLL ){
  2590   2590           if( !pColl ) pColl = db->pDfltColl; 
  2591   2591           sqlite4VdbeAddOp4(v, OP_CollSeq, 0, 0, 0, (char *)pColl, P4_COLLSEQ);
  2592   2592         }
         2593  +      if( pDef->bMatchinfo ){
         2594  +        sqlite4VdbeAddOp1(v, OP_Mifunction, pFarg->a[0].pExpr->iTable);
         2595  +      }
  2593   2596         sqlite4VdbeAddOp4(v, OP_Function, constMask, r1, target,
  2594   2597                           (char*)pDef, P4_FUNCDEF);
  2595   2598         sqlite4VdbeChangeP5(v, (u8)nFarg);
  2596   2599         if( nFarg ){
  2597   2600           sqlite4ReleaseTempRange(pParse, r1, nFarg);
  2598   2601         }
  2599   2602         break;

Changes to src/fts5.c.

    76     76   **   expr := expr NOT expr
    77     77   **   expr := expr AND expr
    78     78   **   expr := expr OR  expr
    79     79   **   expr := LP expr RP
    80     80   */
    81     81   
    82     82   /*
    83         -** Context object used by expression parser.
           83  +** Structure types used by this module.
    84     84   */
    85     85   typedef struct Fts5Expr Fts5Expr;
    86     86   typedef struct Fts5ExprNode Fts5ExprNode;
    87     87   typedef struct Fts5List Fts5List;
    88     88   typedef struct Fts5Parser Fts5Parser;
    89     89   typedef struct Fts5ParserToken Fts5ParserToken;
    90     90   typedef struct Fts5Phrase Fts5Phrase;
................................................................................
   169    169     Fts5ExprNode *pRight;
   170    170     const u8 *aPk;                  /* Primary key of current entry (or null) */
   171    171     int nPk;                        /* Size of aPk[] in bytes */
   172    172   };
   173    173   
   174    174   struct Fts5Expr {
   175    175     Fts5ExprNode *pRoot;
          176  +  int nPhrase;                    /* Number of Fts5Str objects in query */
          177  +  Fts5Str **apPhrase;
   176    178   };
   177    179   
   178    180   /*
   179    181   ** FTS5 specific cursor data.
   180    182   */
   181    183   struct Fts5Cursor {
   182    184     sqlite4 *db;
   183    185     Fts5Info *pInfo;
   184    186     Fts5Expr *pExpr;                /* MATCH expression for this cursor */
   185         -
   186    187     KVByteArray *aKey;              /* Buffer for primary key */
   187    188     int nKeyAlloc;                  /* Bytes allocated at aKey[] */
          189  +
          190  +  KVCursor *pCsr;                 /* Cursor used to retrive values */
          191  +  Mem *aMem;                      /* Array of column values */
   188    192   };
   189    193   
   190    194   /*
   191    195   ** This type is used when reading (decoding) an instance-list.
   192    196   */
   193    197   typedef struct InstanceList InstanceList;
   194    198   struct InstanceList {
................................................................................
   758    762     int nCol,                       /* Size of array azCol[] */
   759    763     const char *zExpr,              /* FTS expression text */
   760    764     Fts5Expr **ppExpr,              /* OUT: Expression object */
   761    765     char **pzErr                    /* OUT: Error message */
   762    766   ){
   763    767     int rc = SQLITE4_OK;
   764    768     Fts5Parser sParse;
          769  +  int nStr = 0;
   765    770     int nExpr;
   766    771     int i;
   767    772     Fts5Expr *pExpr;
   768    773   
   769    774     int nHier = 0;
   770    775     int nHierAlloc = 0;
   771    776     ExprHier *aHier = 0;
................................................................................
   813    818               rc = SQLITE4_NOMEM;
   814    819             }else{
   815    820               pNode->eType = TOKEN_PRIMITIVE;
   816    821               pNode->pPhrase = pPhrase;
   817    822               *pp = pNode;
   818    823             }
   819    824           }
          825  +        nStr++;
   820    826           break;
   821    827         }
   822    828   
   823    829         case TOKEN_AND:
   824    830         case TOKEN_OR:
   825    831         case TOKEN_NOT: {
   826    832           Fts5ExprNode **pp = aHier[nHier-1].ppNode;
................................................................................
   886    892       if( aHier[i].nOpen>0 ) rc = SQLITE4_ERROR;
   887    893     }
   888    894   
   889    895     if( rc!=SQLITE4_OK ){
   890    896       fts5ExpressionFree(db, pExpr);
   891    897       *pzErr = sParse.zErr;
   892    898     }else{
          899  +    pExpr->nPhrase = nStr;
   893    900       *ppExpr = pExpr;
   894    901     }
   895    902     sqlite4DbFree(db, aHier);
   896    903     return rc;
   897    904   }
   898    905   
   899    906   /*
................................................................................
  2294   2301     i = putVarint32(pCsr->aKey, iTbl);
  2295   2302     memcpy(&pCsr->aKey[i], aPk, nPk);
  2296   2303   
  2297   2304     *paKey = pCsr->aKey;
  2298   2305     *pnKey = nReq;
  2299   2306     return SQLITE4_OK;
  2300   2307   }
         2308  +
         2309  +int sqlite4_mi_column_count(sqlite4_context *pCtx, int *pnCol){
         2310  +  int rc = SQLITE4_OK;
         2311  +  if( pCtx->pFts ){
         2312  +    *pnCol = pCtx->pFts->pInfo->nCol;
         2313  +  }else{
         2314  +    rc = SQLITE4_MISUSE;
         2315  +  }
         2316  +  return rc;
         2317  +}
         2318  +
         2319  +int sqlite4_mi_column_size(sqlite4_context *pCtx, int iCol, int *pnToken){
         2320  +  int rc = SQLITE4_OK;
         2321  +  if( pCtx->pFts ){
         2322  +  }else{
         2323  +    rc = SQLITE4_MISUSE;
         2324  +  }
         2325  +  return rc;
         2326  +}
         2327  +
         2328  +int sqlite4_mi_column_value(
         2329  +  sqlite4_context *pCtx, 
         2330  +  int iCol, 
         2331  +  sqlite4_value **ppVal
         2332  +){
         2333  +  int rc = SQLITE4_OK;
         2334  +  if( pCtx->pFts ){
         2335  +  }else{
         2336  +    rc = SQLITE4_MISUSE;
         2337  +  }
         2338  +  return rc;
         2339  +}
         2340  +
         2341  +int sqlite4_mi_phrase_count(sqlite4_context *pCtx, int *pnPhrase){
         2342  +  int rc = SQLITE4_OK;
         2343  +  if( pCtx->pFts ){
         2344  +    *pnPhrase = pCtx->pFts->pExpr->nPhrase;
         2345  +  }else{
         2346  +    rc = SQLITE4_MISUSE;
         2347  +  }
         2348  +  return rc;
         2349  +}
         2350  +
         2351  +int sqlite4_mi_match_count(
         2352  +  sqlite4_context *pCtx, 
         2353  +  int iCol, 
         2354  +  int iPhrase, 
         2355  +  int *pnMatch
         2356  +){
         2357  +  int rc = SQLITE4_OK;
         2358  +  if( pCtx->pFts ){
         2359  +  }else{
         2360  +    rc = SQLITE4_MISUSE;
         2361  +  }
         2362  +  return rc;
         2363  +}
         2364  +
         2365  +int sqlite4_mi_match_offset(
         2366  +  sqlite4_context *pCtx, 
         2367  +  int iCol, 
         2368  +  int iPhrase, 
         2369  +  int iMatch, 
         2370  +  int *piOff
         2371  +){
         2372  +}
         2373  +
         2374  +int sqlite4_mi_total_match_count(
         2375  +  sqlite4_context *pCtx, 
         2376  +  int iCol, 
         2377  +  int iPhrase, 
         2378  +  int *pnMatch, 
         2379  +  int *pnDoc
         2380  +){
         2381  +}
         2382  +
         2383  +int sqlite4_mi_total_size(sqlite4_context *pCtx, int iCol, int *pnToken){
         2384  +}
         2385  +
         2386  +int sqlite4_mi_total_count(sqlite4_context *pCtx, int *pnRow){
         2387  +}
  2301   2388   
  2302   2389   /**************************************************************************
  2303   2390   ***************************************************************************
  2304   2391   ** Below this point is test code.
  2305   2392   */
  2306   2393   #ifdef SQLITE4_TEST
  2307   2394   static int fts5PrintExprNode(sqlite4 *, const char **, Fts5ExprNode *, char **);

Changes to src/fts5func.c.

     8      8   **    May you find forgiveness for yourself and forgive others.
     9      9   **    May you share freely, never taking more than you give.
    10     10   **
    11     11   *************************************************************************
    12     12   */
    13     13   
    14     14   #include "sqliteInt.h"
           15  +
           16  +static char fts5Tolower(char c){
           17  +  if( c>='A' && c<='Z' ) c = c + ('a' - 'A');
           18  +  return c;
           19  +}
    15     20   
    16     21   static int fts5SimpleCreate(
    17     22     void *pCtx, 
    18     23     const char **azArg, 
    19     24     int nArg, 
    20     25     sqlite4_tokenizer **pp
    21     26   ){
................................................................................
    23     28     return SQLITE4_OK;
    24     29   }
    25     30   
    26     31   static int fts5SimpleDestroy(sqlite4_tokenizer *p){
    27     32     return SQLITE4_OK;
    28     33   }
    29     34   
    30         -static char fts5Tolower(char c){
    31         -  if( c>='A' && c<='Z' ) c = c + ('a' - 'A');
    32         -  return c;
           35  +static void fts5Rank(sqlite4_context *pCtx, int nArg, sqlite4_value **apArg){
           36  +}
           37  +
           38  +static void fts5Snippet(sqlite4_context *pCtx, int nArg, sqlite4_value **apArg){
    33     39   }
    34     40   
    35     41   static int fts5SimpleTokenize(
    36     42     void *pCtx, 
    37     43     sqlite4_tokenizer *p,
    38     44     const char *zDoc,
    39     45     int nDoc,
................................................................................
    71     77     sqlite4_env *pEnv = sqlite4_db_env(db);
    72     78   
    73     79     rc = sqlite4_create_tokenizer(db, "simple", (void *)pEnv, 
    74     80         fts5SimpleCreate, fts5SimpleTokenize, fts5SimpleDestroy
    75     81     );
    76     82     if( rc!=SQLITE4_OK ) return rc;
    77     83   
           84  +  rc = sqlite4_create_mi_function(db, "rank", 0, SQLITE4_UTF8, 0, fts5Rank, 0);
           85  +  if( rc!=SQLITE4_OK ) return rc;
           86  +
           87  +  rc = sqlite4_create_mi_function(
           88  +      db, "snippet", -1, SQLITE4_UTF8, 0, fts5Snippet, 0
           89  +  );
    78     90     return rc;
    79     91   }
    80     92   

Changes to src/main.c.

  1048   1048     if( pArg && pArg->nRef==0 ){
  1049   1049       assert( rc!=SQLITE4_OK );
  1050   1050       xDestroy(p);
  1051   1051       sqlite4DbFree(db, pArg);
  1052   1052     }
  1053   1053   
  1054   1054    out:
         1055  +  rc = sqlite4ApiExit(db, rc);
         1056  +  sqlite4_mutex_leave(db->mutex);
         1057  +  return rc;
         1058  +}
         1059  +
         1060  +int sqlite4_create_mi_function(
         1061  +  sqlite4 *db,
         1062  +  const char *zFunc,
         1063  +  int nArg,
         1064  +  int enc,
         1065  +  void *p,
         1066  +  void (*xFunc)(sqlite4_context*,int,sqlite4_value **),
         1067  +  void (*xDestroy)(void *)
         1068  +){
         1069  +  int rc;
         1070  +  int n;
         1071  +
         1072  +  n = nArg + (nArg>=0);
         1073  +  sqlite4_mutex_enter(db->mutex);
         1074  +  rc = sqlite4_create_function_v2(db, zFunc, n, enc, p, xFunc, 0,0,xDestroy);
         1075  +  if( rc==SQLITE4_OK ){
         1076  +    FuncDef *p = sqlite4FindFunction(db, zFunc, -1, n, enc, 0);
         1077  +    p->bMatchinfo = 1;
         1078  +  }
  1055   1079     rc = sqlite4ApiExit(db, rc);
  1056   1080     sqlite4_mutex_leave(db->mutex);
  1057   1081     return rc;
  1058   1082   }
  1059   1083   
  1060   1084   #ifndef SQLITE4_OMIT_UTF16
  1061   1085   int sqlite4_create_function16(

Changes to src/resolve.c.

   431    431       testcase( iCol==BMS );
   432    432       testcase( iCol==BMS-1 );
   433    433       pItem->colUsed |= ((Bitmask)1)<<(iCol>=BMS ? BMS-1 : iCol);
   434    434       ExprSetProperty(p, EP_Resolved);
   435    435     }
   436    436     return p;
   437    437   }
          438  +
          439  +static void resolveMatchArg(Parse *pParse, NameContext *pNC, Expr *pExpr){
          440  +  SrcList *pSrc = pNC->pSrcList;
          441  +  SrcListItem *pItem;
          442  +  char *zLhs;
          443  +  int i;
          444  +  Index *pIdx;
          445  +
          446  +  if( pExpr->op!=TK_ID || pSrc==0 || pExpr==0 ){
          447  +    sqlite4ErrorMsg(pParse, "first argument xxx must be a table name");
          448  +    return;
          449  +  }
          450  +  zLhs = pExpr->u.zToken;
          451  +
          452  +  for(i=0; i<pSrc->nSrc; i++){
          453  +    pItem = &pSrc->a[i];
          454  +    if( pItem->zAlias && sqlite4StrICmp(zLhs, pItem->zAlias)==0 ) break;
          455  +    if( pItem->zAlias==0 && sqlite4StrICmp(zLhs, pItem->zName)==0 ) break;
          456  +  }
          457  +  if( i==pSrc->nSrc ){
          458  +    sqlite4ErrorMsg(pParse, "no such table: %s", zLhs);
          459  +    return;
          460  +  }
          461  +
          462  +  pExpr->op = TK_NULL;
          463  +  pExpr->iTable = pItem->iCursor;
          464  +  ExprSetProperty(pExpr, EP_Resolved);
          465  +}
   438    466   
   439    467   static void resolveMatch(Parse *pParse, NameContext *pNC, Expr *pExpr){
   440    468     Expr *pLeft = pExpr->pLeft;
   441    469     SrcList *pSrc = pNC->pSrcList;
   442    470     SrcListItem *pItem;
   443    471     char *zLhs;
   444    472     int i;
................................................................................
   612    640                nId, zId);
   613    641           pNC->nErr++;
   614    642         }
   615    643         if( is_agg ){
   616    644           pExpr->op = TK_AGG_FUNCTION;
   617    645           pNC->hasAgg = 1;
   618    646         }
   619         -      if( is_agg ) pNC->allowAgg = 0;
   620         -      sqlite4WalkExprList(pWalker, pList);
   621         -      if( is_agg ) pNC->allowAgg = 1;
          647  +
          648  +      if( pParse->nErr==0 ){
          649  +        if( pDef->bMatchinfo ){
          650  +          resolveMatchArg(pParse, pNC, n>0 ? pList->a[0].pExpr : 0);
          651  +        }
          652  +        if( is_agg ) pNC->allowAgg = 0;
          653  +        sqlite4WalkExprList(pWalker, pList);
          654  +        if( is_agg ) pNC->allowAgg = 1;
          655  +      }
          656  +
   622    657         /* FIX ME:  Compute pExpr->affinity based on the expected return
   623    658         ** type of the function 
   624    659         */
   625    660         return WRC_Prune;
   626    661       }
   627    662   #ifndef SQLITE4_OMIT_SUBQUERY
   628    663       case TK_SELECT:

Changes to src/sqlite.h.in.

  4400   4400         const char*, int, 
  4401   4401         int(*x)(void *ctx, int iWeight, int iOff, 
  4402   4402                 const char *zToken, int nToken, int iSrc, int nSrc)
  4403   4403     ),
  4404   4404     int (*xDestroy)(sqlite4_tokenizer *)
  4405   4405   );
  4406   4406   
         4407  +/*
         4408  +** CAPI4REF: Register a matchinfo function.
         4409  +*/
         4410  +int sqlite4_create_mi_function(
         4411  +  sqlite4 *db,
         4412  +  const char *zFunc,
         4413  +  int nArg,
         4414  +  int enc,
         4415  +  void *p,
         4416  +  void (*xFunc)(sqlite4_context*,int,sqlite4_value **),
         4417  +  void (*xDestroy)(void *)
         4418  +);
         4419  +
         4420  +/*
         4421  +** Special functions that may be called from within matchinfo UDFs. All
         4422  +** return an SQLite error code - SQLITE4_OK if successful, or some other
         4423  +** error code otherwise.
         4424  +**
         4425  +** sqlite4_mi_column_count():
         4426  +**   Set *pnCol to the number of columns in the queried table.
         4427  +**
         4428  +** sqlite4_mi_column_size():
         4429  +**   Set *pnToken to the number of tokens in the value stored in column iCol 
         4430  +**   of the current row.
         4431  +**
         4432  +** sqlite4_mi_column_value():
         4433  +**   Set *ppVal to point to an sqlite4_value object containing the value
         4434  +**   read from column iCol of the current row. This object is valid until
         4435  +**   the function callback returns.
         4436  +**
         4437  +** sqlite4_mi_phrase_count():
         4438  +**   Set *pnPhrase to the number of phrases in the query.
         4439  +**
         4440  +** sqlite4_mi_match_count():
         4441  +**   Set *pn to the number of occurences of phrase iPhrase in column iCol of
         4442  +**   the current row.
         4443  +**
         4444  +** sqlite4_mi_total_match_count():
         4445  +**   Set *pnMatch to the total number of occurrences of phrase iPhrase
         4446  +**   in column iCol of all rows in the indexed table. Set *pnDoc to the
         4447  +**   number of rows that contain at least one match for phrase iPhrase in
         4448  +**   column iCol.
         4449  +**
         4450  +** sqlite4_mi_match_offset():
         4451  +**   Set *piOff to the token offset of the iMatch'th instance of phrase
         4452  +**   iPhrase in column iCol of the current row. If any parameter is out
         4453  +**   of range (i.e. too large) it is not an error. In this case *piOff is 
         4454  +**   set to -1 before returning.
         4455  +**   
         4456  +** sqlite4_mi_total_size():
         4457  +**   Set *pnToken to the total number of tokens in column iCol of all rows
         4458  +**   in the indexed table.
         4459  +**
         4460  +** sqlite4_mi_total_count():
         4461  +**   Set *pnRow to the total number of rows in the indexed table.
         4462  +*/
         4463  +int sqlite4_mi_column_count(sqlite4_context *, int *pnCol);
         4464  +int sqlite4_mi_column_size(sqlite4_context *, int iCol, int *pnToken);
         4465  +int sqlite4_mi_column_value(sqlite4_context *, int iCol, sqlite4_value **ppVal);
         4466  +
         4467  +int sqlite4_mi_phrase_count(sqlite4_context *, int *pnPhrase);
         4468  +int sqlite4_mi_match_count(sqlite4_context *, int iCol, int iPhrase, int *pn);
         4469  +int sqlite4_mi_match_offset(
         4470  +    sqlite4_context *, int iCol, int iPhrase, int iMatch, int *piOff); 
         4471  +
         4472  +int sqlite4_mi_total_match_count(
         4473  +    sqlite4_context *, int iCol, int iPhrase, int *pnMatch, int *pnDoc);
         4474  +
         4475  +int sqlite4_mi_total_size(sqlite4_context *, int iCol, int *pnToken);
         4476  +int sqlite4_mi_total_count(sqlite4_context *, int *pnRow);
         4477  +
  4407   4478   /*
  4408   4479   ** Undo the hack that converts floating point types to integer for
  4409   4480   ** builds on processors without floating point support.
  4410   4481   */
  4411   4482   #ifdef SQLITE4_OMIT_FLOATING_POINT
  4412   4483   # undef double
  4413   4484   #endif
  4414   4485   
  4415   4486   #ifdef __cplusplus
  4416   4487   }  /* End of the 'extern "C"' block */
  4417   4488   #endif
  4418   4489   #endif

Changes to src/sqliteInt.h.

   635    635     FuncDef *pSameName;  /* Next with a different name but the same hash */
   636    636     void (*xFunc)(sqlite4_context*,int,sqlite4_value**); /* Regular function */
   637    637     void (*xStep)(sqlite4_context*,int,sqlite4_value**); /* Aggregate step */
   638    638     void (*xFinalize)(sqlite4_context*);                /* Aggregate finalizer */
   639    639     char *zName;         /* SQL name of the function. */
   640    640     FuncDef *pNextName;  /* Next function with a different name */
   641    641     FuncDestructor *pDestructor;   /* Reference counted destructor function */
          642  +  u8 bMatchinfo;       /* True for matchinfo function */
   642    643   };
   643    644   
   644    645   /*
   645    646   ** A table of SQL functions.  
   646    647   **
   647    648   ** The content is a linked list of FuncDef structures with branches.  When
   648    649   ** there are two or more FuncDef objects with the same name, they are 

Changes to src/vdbe.c.

  1285   1285   ** to retrieve the collation sequence set by this opcode is not available
  1286   1286   ** publicly, only to user functions defined in func.c.
  1287   1287   */
  1288   1288   case OP_CollSeq: {
  1289   1289     assert( pOp->p4type==P4_COLLSEQ );
  1290   1290     break;
  1291   1291   }
         1292  +
         1293  +/* Opcode: Mifunction P1
         1294  +*/
         1295  +case OP_Mifunction: {
         1296  +  pc++;
         1297  +  pOp++;
         1298  +  /* fall through to OP_Function */
         1299  +};
  1292   1300   
  1293   1301   /* Opcode: Function P1 P2 P3 P4 P5
  1294   1302   **
  1295   1303   ** Invoke a user function (P4 is a pointer to a Function structure that
  1296   1304   ** defines the function) with P5 arguments taken from register P2 and
  1297   1305   ** successors.  The result of the function is stored in register P3.
  1298   1306   ** Register P3 must not be one of the function inputs.
................................................................................
  1340   1348       ctx.pFunc = ctx.pVdbeFunc->pFunc;
  1341   1349     }
  1342   1350   
  1343   1351     ctx.s.flags = MEM_Null;
  1344   1352     ctx.s.db = db;
  1345   1353     ctx.s.xDel = 0;
  1346   1354     ctx.s.zMalloc = 0;
         1355  +  if( pOp[-1].opcode==OP_Mifunction ){
         1356  +    ctx.pFts = p->apCsr[pOp[-1].p1]->pFts;
         1357  +    apVal++;
         1358  +    n--;
         1359  +  }else{
         1360  +    ctx.pFts = 0;
         1361  +  }
  1347   1362   
  1348   1363     /* The output cell may already have a buffer allocated. Move
  1349   1364     ** the pointer to ctx.s so in case the user-function can use
  1350   1365     ** the already allocated buffer instead of allocating a new one.
  1351   1366     */
  1352   1367     sqlite4VdbeMemMove(&ctx.s, pOut);
  1353   1368     MemSetTypeFlag(&ctx.s, MEM_Null);

Changes to src/vdbeInt.h.

   237    237   struct sqlite4_context {
   238    238     FuncDef *pFunc;       /* Pointer to function information.  MUST BE FIRST */
   239    239     VdbeFunc *pVdbeFunc;  /* Auxilary data, if created. */
   240    240     Mem s;                /* The return value is stored here */
   241    241     Mem *pMem;            /* Memory cell used to store aggregate context */
   242    242     int isError;          /* Error code returned by the function. */
   243    243     CollSeq *pColl;       /* Collating sequence */
          244  +  Fts5Cursor *pFts;     /* fts5 cursor for matchinfo functions */
   244    245   };
   245    246   
   246    247   /*
   247    248   ** An Explain object accumulates indented output which is helpful
   248    249   ** in describing recursive data structures.
   249    250   */
   250    251   struct Explain {

Changes to src/where.c.

  5225   5225         while( pOp<pEnd ){
  5226   5226           if( pOp->p1==pLevel->iTabCur && pOp->opcode==OP_Column ){
  5227   5227             pOp->p1 = pLevel->iIdxCur;
  5228   5228           }
  5229   5229           pOp++;
  5230   5230         }
  5231   5231       }
         5232  +
         5233  +    if( (pLevel->plan.wsFlags & WHERE_INDEXED)
         5234  +     && (pLevel->plan.u.pIdx->eIndexType==SQLITE4_INDEX_FTS5)
         5235  +    ){
         5236  +      VdbeOp *pOp;
         5237  +      VdbeOp *pEnd;
         5238  +
         5239  +      assert( pLevel->iTabCur!=pLevel->iIdxCur );
         5240  +      pOp = sqlite4VdbeGetOp(v, pWInfo->iTop);
         5241  +      pEnd = &pOp[sqlite4VdbeCurrentAddr(v) - pWInfo->iTop];
         5242  +
         5243  +      while( pOp<pEnd ){
         5244  +        if( pOp->p1==pLevel->iTabCur && pOp->opcode==OP_Mifunction ){
         5245  +          pOp->p1 = pLevel->iIdxCur;
         5246  +        }
         5247  +        pOp++;
         5248  +      }
         5249  +    }
  5232   5250     }
  5233   5251   
  5234   5252     /* Final cleanup
  5235   5253     */
  5236   5254     pParse->nQueryLoop = pWInfo->savedNQueryLoop;
  5237   5255     whereInfoFree(db, pWInfo);
  5238   5256     return;
  5239   5257   }

Changes to test/fts5create.test.

    70     70     CREATE INDEX ft ON t2 USING fts5(tukenizer=simple);
    71     71   } {1 {unrecognized argument: "tukenizer"}}
    72     72   
    73     73   do_catchsql_test 2.3 { 
    74     74     CREATE INDEX ft ON t2 USING fts5("a b c");
    75     75   } {1 {unrecognized argument: "a b c"}}
    76     76   
    77         -do_catchsql_test 2.4 { 
           77  +do_catchsql_test 2.4 {
    78     78     CREATE INDEX ft ON t2 USING fts5(tokenizer="nosuch");
    79     79   } {1 {no such tokenizer: "nosuch"}}
    80     80   
    81     81   finish_test
    82     82   
    83     83   
    84     84   

Changes to test/fts5query1.test.

   130    130     2 {a:a}  {1}
   131    131     3 {b:a}  {2}
   132    132     4 {c:a}  {1 2}
   133    133     5 {a:a*} {1}
   134    134   } {
   135    135     do_execsql_test 7.$tn {SELECT docid FROM t7 WHERE t7 MATCH $expr} $res
   136    136   }
          137  +
          138  +#-------------------------------------------------------------------------
          139  +#
          140  +do_execsql_test 8.0 {
          141  +  CREATE TABLE t8(a PRIMARY KEY, b, c);
          142  +  CREATE INDEX i8 ON t8 USING fts5();
          143  +  INSERT INTO t8 VALUES('one', 'a b c', 'a a a');
          144  +  INSERT INTO t8 VALUES('two', 'd e f', 'b b b');
          145  +}
          146  +
          147  +do_execsql_test 8.1 {
          148  +  SELECT rank(t8) FROM t8 WHERE t8 MATCH 'b a'
          149  +}
   137    150   
   138    151   finish_test
   139    152