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
Timelines: family | ancestors | descendants | both | matchinfo
Files: files | file ages | folders
SHA1: a235305d42ed0b17be6e18d4932d97046f9c03da
User & Date: dan 2013-01-01 19:56:04.682
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
Unified Diff Ignore Whitespace Patch
Changes to src/expr.c.
2586
2587
2588
2589
2590
2591
2592



2593
2594
2595
2596
2597
2598
2599
          pColl = sqlite4ExprCollSeq(pParse, pFarg->a[i].pExpr);
        }
      }
      if( pDef->flags & SQLITE4_FUNC_NEEDCOLL ){
        if( !pColl ) pColl = db->pDfltColl; 
        sqlite4VdbeAddOp4(v, OP_CollSeq, 0, 0, 0, (char *)pColl, P4_COLLSEQ);
      }



      sqlite4VdbeAddOp4(v, OP_Function, constMask, r1, target,
                        (char*)pDef, P4_FUNCDEF);
      sqlite4VdbeChangeP5(v, (u8)nFarg);
      if( nFarg ){
        sqlite4ReleaseTempRange(pParse, r1, nFarg);
      }
      break;







>
>
>







2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
          pColl = sqlite4ExprCollSeq(pParse, pFarg->a[i].pExpr);
        }
      }
      if( pDef->flags & SQLITE4_FUNC_NEEDCOLL ){
        if( !pColl ) pColl = db->pDfltColl; 
        sqlite4VdbeAddOp4(v, OP_CollSeq, 0, 0, 0, (char *)pColl, P4_COLLSEQ);
      }
      if( pDef->bMatchinfo ){
        sqlite4VdbeAddOp1(v, OP_Mifunction, pFarg->a[0].pExpr->iTable);
      }
      sqlite4VdbeAddOp4(v, OP_Function, constMask, r1, target,
                        (char*)pDef, P4_FUNCDEF);
      sqlite4VdbeChangeP5(v, (u8)nFarg);
      if( nFarg ){
        sqlite4ReleaseTempRange(pParse, r1, nFarg);
      }
      break;
Changes to src/fts5.c.
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
**   expr := expr NOT expr
**   expr := expr AND expr
**   expr := expr OR  expr
**   expr := LP expr RP
*/

/*
** Context object used by expression parser.
*/
typedef struct Fts5Expr Fts5Expr;
typedef struct Fts5ExprNode Fts5ExprNode;
typedef struct Fts5List Fts5List;
typedef struct Fts5Parser Fts5Parser;
typedef struct Fts5ParserToken Fts5ParserToken;
typedef struct Fts5Phrase Fts5Phrase;







|







76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
**   expr := expr NOT expr
**   expr := expr AND expr
**   expr := expr OR  expr
**   expr := LP expr RP
*/

/*
** Structure types used by this module.
*/
typedef struct Fts5Expr Fts5Expr;
typedef struct Fts5ExprNode Fts5ExprNode;
typedef struct Fts5List Fts5List;
typedef struct Fts5Parser Fts5Parser;
typedef struct Fts5ParserToken Fts5ParserToken;
typedef struct Fts5Phrase Fts5Phrase;
169
170
171
172
173
174
175


176
177
178
179
180
181
182
183
184
185
186
187



188
189
190
191
192
193
194
  Fts5ExprNode *pRight;
  const u8 *aPk;                  /* Primary key of current entry (or null) */
  int nPk;                        /* Size of aPk[] in bytes */
};

struct Fts5Expr {
  Fts5ExprNode *pRoot;


};

/*
** FTS5 specific cursor data.
*/
struct Fts5Cursor {
  sqlite4 *db;
  Fts5Info *pInfo;
  Fts5Expr *pExpr;                /* MATCH expression for this cursor */

  KVByteArray *aKey;              /* Buffer for primary key */
  int nKeyAlloc;                  /* Bytes allocated at aKey[] */



};

/*
** This type is used when reading (decoding) an instance-list.
*/
typedef struct InstanceList InstanceList;
struct InstanceList {







>
>









<


>
>
>







169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186

187
188
189
190
191
192
193
194
195
196
197
198
  Fts5ExprNode *pRight;
  const u8 *aPk;                  /* Primary key of current entry (or null) */
  int nPk;                        /* Size of aPk[] in bytes */
};

struct Fts5Expr {
  Fts5ExprNode *pRoot;
  int nPhrase;                    /* Number of Fts5Str objects in query */
  Fts5Str **apPhrase;
};

/*
** FTS5 specific cursor data.
*/
struct Fts5Cursor {
  sqlite4 *db;
  Fts5Info *pInfo;
  Fts5Expr *pExpr;                /* MATCH expression for this cursor */

  KVByteArray *aKey;              /* Buffer for primary key */
  int nKeyAlloc;                  /* Bytes allocated at aKey[] */

  KVCursor *pCsr;                 /* Cursor used to retrive values */
  Mem *aMem;                      /* Array of column values */
};

/*
** This type is used when reading (decoding) an instance-list.
*/
typedef struct InstanceList InstanceList;
struct InstanceList {
758
759
760
761
762
763
764

765
766
767
768
769
770
771
  int nCol,                       /* Size of array azCol[] */
  const char *zExpr,              /* FTS expression text */
  Fts5Expr **ppExpr,              /* OUT: Expression object */
  char **pzErr                    /* OUT: Error message */
){
  int rc = SQLITE4_OK;
  Fts5Parser sParse;

  int nExpr;
  int i;
  Fts5Expr *pExpr;

  int nHier = 0;
  int nHierAlloc = 0;
  ExprHier *aHier = 0;







>







762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
  int nCol,                       /* Size of array azCol[] */
  const char *zExpr,              /* FTS expression text */
  Fts5Expr **ppExpr,              /* OUT: Expression object */
  char **pzErr                    /* OUT: Error message */
){
  int rc = SQLITE4_OK;
  Fts5Parser sParse;
  int nStr = 0;
  int nExpr;
  int i;
  Fts5Expr *pExpr;

  int nHier = 0;
  int nHierAlloc = 0;
  ExprHier *aHier = 0;
813
814
815
816
817
818
819

820
821
822
823
824
825
826
            rc = SQLITE4_NOMEM;
          }else{
            pNode->eType = TOKEN_PRIMITIVE;
            pNode->pPhrase = pPhrase;
            *pp = pNode;
          }
        }

        break;
      }

      case TOKEN_AND:
      case TOKEN_OR:
      case TOKEN_NOT: {
        Fts5ExprNode **pp = aHier[nHier-1].ppNode;







>







818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
            rc = SQLITE4_NOMEM;
          }else{
            pNode->eType = TOKEN_PRIMITIVE;
            pNode->pPhrase = pPhrase;
            *pp = pNode;
          }
        }
        nStr++;
        break;
      }

      case TOKEN_AND:
      case TOKEN_OR:
      case TOKEN_NOT: {
        Fts5ExprNode **pp = aHier[nHier-1].ppNode;
886
887
888
889
890
891
892

893
894
895
896
897
898
899
    if( aHier[i].nOpen>0 ) rc = SQLITE4_ERROR;
  }

  if( rc!=SQLITE4_OK ){
    fts5ExpressionFree(db, pExpr);
    *pzErr = sParse.zErr;
  }else{

    *ppExpr = pExpr;
  }
  sqlite4DbFree(db, aHier);
  return rc;
}

/*







>







892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
    if( aHier[i].nOpen>0 ) rc = SQLITE4_ERROR;
  }

  if( rc!=SQLITE4_OK ){
    fts5ExpressionFree(db, pExpr);
    *pzErr = sParse.zErr;
  }else{
    pExpr->nPhrase = nStr;
    *ppExpr = pExpr;
  }
  sqlite4DbFree(db, aHier);
  return rc;
}

/*
2294
2295
2296
2297
2298
2299
2300
















































































2301
2302
2303
2304
2305
2306
2307
  i = putVarint32(pCsr->aKey, iTbl);
  memcpy(&pCsr->aKey[i], aPk, nPk);

  *paKey = pCsr->aKey;
  *pnKey = nReq;
  return SQLITE4_OK;
}

















































































/**************************************************************************
***************************************************************************
** Below this point is test code.
*/
#ifdef SQLITE4_TEST
static int fts5PrintExprNode(sqlite4 *, const char **, Fts5ExprNode *, char **);







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
  i = putVarint32(pCsr->aKey, iTbl);
  memcpy(&pCsr->aKey[i], aPk, nPk);

  *paKey = pCsr->aKey;
  *pnKey = nReq;
  return SQLITE4_OK;
}

int sqlite4_mi_column_count(sqlite4_context *pCtx, int *pnCol){
  int rc = SQLITE4_OK;
  if( pCtx->pFts ){
    *pnCol = pCtx->pFts->pInfo->nCol;
  }else{
    rc = SQLITE4_MISUSE;
  }
  return rc;
}

int sqlite4_mi_column_size(sqlite4_context *pCtx, int iCol, int *pnToken){
  int rc = SQLITE4_OK;
  if( pCtx->pFts ){
  }else{
    rc = SQLITE4_MISUSE;
  }
  return rc;
}

int sqlite4_mi_column_value(
  sqlite4_context *pCtx, 
  int iCol, 
  sqlite4_value **ppVal
){
  int rc = SQLITE4_OK;
  if( pCtx->pFts ){
  }else{
    rc = SQLITE4_MISUSE;
  }
  return rc;
}

int sqlite4_mi_phrase_count(sqlite4_context *pCtx, int *pnPhrase){
  int rc = SQLITE4_OK;
  if( pCtx->pFts ){
    *pnPhrase = pCtx->pFts->pExpr->nPhrase;
  }else{
    rc = SQLITE4_MISUSE;
  }
  return rc;
}

int sqlite4_mi_match_count(
  sqlite4_context *pCtx, 
  int iCol, 
  int iPhrase, 
  int *pnMatch
){
  int rc = SQLITE4_OK;
  if( pCtx->pFts ){
  }else{
    rc = SQLITE4_MISUSE;
  }
  return rc;
}

int sqlite4_mi_match_offset(
  sqlite4_context *pCtx, 
  int iCol, 
  int iPhrase, 
  int iMatch, 
  int *piOff
){
}

int sqlite4_mi_total_match_count(
  sqlite4_context *pCtx, 
  int iCol, 
  int iPhrase, 
  int *pnMatch, 
  int *pnDoc
){
}

int sqlite4_mi_total_size(sqlite4_context *pCtx, int iCol, int *pnToken){
}

int sqlite4_mi_total_count(sqlite4_context *pCtx, int *pnRow){
}

/**************************************************************************
***************************************************************************
** Below this point is test code.
*/
#ifdef SQLITE4_TEST
static int fts5PrintExprNode(sqlite4 *, const char **, Fts5ExprNode *, char **);
Changes to src/fts5func.c.
8
9
10
11
12
13
14





15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

31
32

33
34
35
36
37
38
39
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
*/

#include "sqliteInt.h"






static int fts5SimpleCreate(
  void *pCtx, 
  const char **azArg, 
  int nArg, 
  sqlite4_tokenizer **pp
){
  *pp = (sqlite4_tokenizer *)pCtx;
  return SQLITE4_OK;
}

static int fts5SimpleDestroy(sqlite4_tokenizer *p){
  return SQLITE4_OK;
}

static char fts5Tolower(char c){

  if( c>='A' && c<='Z' ) c = c + ('a' - 'A');
  return c;

}

static int fts5SimpleTokenize(
  void *pCtx, 
  sqlite4_tokenizer *p,
  const char *zDoc,
  int nDoc,







>
>
>
>
>















|
>
|
<
>







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

38
39
40
41
42
43
44
45
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
*/

#include "sqliteInt.h"

static char fts5Tolower(char c){
  if( c>='A' && c<='Z' ) c = c + ('a' - 'A');
  return c;
}

static int fts5SimpleCreate(
  void *pCtx, 
  const char **azArg, 
  int nArg, 
  sqlite4_tokenizer **pp
){
  *pp = (sqlite4_tokenizer *)pCtx;
  return SQLITE4_OK;
}

static int fts5SimpleDestroy(sqlite4_tokenizer *p){
  return SQLITE4_OK;
}

static void fts5Rank(sqlite4_context *pCtx, int nArg, sqlite4_value **apArg){
}


static void fts5Snippet(sqlite4_context *pCtx, int nArg, sqlite4_value **apArg){
}

static int fts5SimpleTokenize(
  void *pCtx, 
  sqlite4_tokenizer *p,
  const char *zDoc,
  int nDoc,
71
72
73
74
75
76
77






78
79
80
  sqlite4_env *pEnv = sqlite4_db_env(db);

  rc = sqlite4_create_tokenizer(db, "simple", (void *)pEnv, 
      fts5SimpleCreate, fts5SimpleTokenize, fts5SimpleDestroy
  );
  if( rc!=SQLITE4_OK ) return rc;







  return rc;
}








>
>
>
>
>
>



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
  sqlite4_env *pEnv = sqlite4_db_env(db);

  rc = sqlite4_create_tokenizer(db, "simple", (void *)pEnv, 
      fts5SimpleCreate, fts5SimpleTokenize, fts5SimpleDestroy
  );
  if( rc!=SQLITE4_OK ) return rc;

  rc = sqlite4_create_mi_function(db, "rank", 0, SQLITE4_UTF8, 0, fts5Rank, 0);
  if( rc!=SQLITE4_OK ) return rc;

  rc = sqlite4_create_mi_function(
      db, "snippet", -1, SQLITE4_UTF8, 0, fts5Snippet, 0
  );
  return rc;
}

Changes to src/main.c.
1048
1049
1050
1051
1052
1053
1054
























1055
1056
1057
1058
1059
1060
1061
  if( pArg && pArg->nRef==0 ){
    assert( rc!=SQLITE4_OK );
    xDestroy(p);
    sqlite4DbFree(db, pArg);
  }

 out:
























  rc = sqlite4ApiExit(db, rc);
  sqlite4_mutex_leave(db->mutex);
  return rc;
}

#ifndef SQLITE4_OMIT_UTF16
int sqlite4_create_function16(







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
  if( pArg && pArg->nRef==0 ){
    assert( rc!=SQLITE4_OK );
    xDestroy(p);
    sqlite4DbFree(db, pArg);
  }

 out:
  rc = sqlite4ApiExit(db, rc);
  sqlite4_mutex_leave(db->mutex);
  return rc;
}

int sqlite4_create_mi_function(
  sqlite4 *db,
  const char *zFunc,
  int nArg,
  int enc,
  void *p,
  void (*xFunc)(sqlite4_context*,int,sqlite4_value **),
  void (*xDestroy)(void *)
){
  int rc;
  int n;

  n = nArg + (nArg>=0);
  sqlite4_mutex_enter(db->mutex);
  rc = sqlite4_create_function_v2(db, zFunc, n, enc, p, xFunc, 0,0,xDestroy);
  if( rc==SQLITE4_OK ){
    FuncDef *p = sqlite4FindFunction(db, zFunc, -1, n, enc, 0);
    p->bMatchinfo = 1;
  }
  rc = sqlite4ApiExit(db, rc);
  sqlite4_mutex_leave(db->mutex);
  return rc;
}

#ifndef SQLITE4_OMIT_UTF16
int sqlite4_create_function16(
Changes to src/resolve.c.
431
432
433
434
435
436
437




























438
439
440
441
442
443
444
    testcase( iCol==BMS );
    testcase( iCol==BMS-1 );
    pItem->colUsed |= ((Bitmask)1)<<(iCol>=BMS ? BMS-1 : iCol);
    ExprSetProperty(p, EP_Resolved);
  }
  return p;
}





























static void resolveMatch(Parse *pParse, NameContext *pNC, Expr *pExpr){
  Expr *pLeft = pExpr->pLeft;
  SrcList *pSrc = pNC->pSrcList;
  SrcListItem *pItem;
  char *zLhs;
  int i;







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
    testcase( iCol==BMS );
    testcase( iCol==BMS-1 );
    pItem->colUsed |= ((Bitmask)1)<<(iCol>=BMS ? BMS-1 : iCol);
    ExprSetProperty(p, EP_Resolved);
  }
  return p;
}

static void resolveMatchArg(Parse *pParse, NameContext *pNC, Expr *pExpr){
  SrcList *pSrc = pNC->pSrcList;
  SrcListItem *pItem;
  char *zLhs;
  int i;
  Index *pIdx;

  if( pExpr->op!=TK_ID || pSrc==0 || pExpr==0 ){
    sqlite4ErrorMsg(pParse, "first argument xxx must be a table name");
    return;
  }
  zLhs = pExpr->u.zToken;

  for(i=0; i<pSrc->nSrc; i++){
    pItem = &pSrc->a[i];
    if( pItem->zAlias && sqlite4StrICmp(zLhs, pItem->zAlias)==0 ) break;
    if( pItem->zAlias==0 && sqlite4StrICmp(zLhs, pItem->zName)==0 ) break;
  }
  if( i==pSrc->nSrc ){
    sqlite4ErrorMsg(pParse, "no such table: %s", zLhs);
    return;
  }

  pExpr->op = TK_NULL;
  pExpr->iTable = pItem->iCursor;
  ExprSetProperty(pExpr, EP_Resolved);
}

static void resolveMatch(Parse *pParse, NameContext *pNC, Expr *pExpr){
  Expr *pLeft = pExpr->pLeft;
  SrcList *pSrc = pNC->pSrcList;
  SrcListItem *pItem;
  char *zLhs;
  int i;
612
613
614
615
616
617
618





619
620
621


622
623
624
625
626
627
628
             nId, zId);
        pNC->nErr++;
      }
      if( is_agg ){
        pExpr->op = TK_AGG_FUNCTION;
        pNC->hasAgg = 1;
      }





      if( is_agg ) pNC->allowAgg = 0;
      sqlite4WalkExprList(pWalker, pList);
      if( is_agg ) pNC->allowAgg = 1;


      /* FIX ME:  Compute pExpr->affinity based on the expected return
      ** type of the function 
      */
      return WRC_Prune;
    }
#ifndef SQLITE4_OMIT_SUBQUERY
    case TK_SELECT:







>
>
>
>
>
|
|
|
>
>







640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
             nId, zId);
        pNC->nErr++;
      }
      if( is_agg ){
        pExpr->op = TK_AGG_FUNCTION;
        pNC->hasAgg = 1;
      }

      if( pParse->nErr==0 ){
        if( pDef->bMatchinfo ){
          resolveMatchArg(pParse, pNC, n>0 ? pList->a[0].pExpr : 0);
        }
        if( is_agg ) pNC->allowAgg = 0;
        sqlite4WalkExprList(pWalker, pList);
        if( is_agg ) pNC->allowAgg = 1;
      }

      /* FIX ME:  Compute pExpr->affinity based on the expected return
      ** type of the function 
      */
      return WRC_Prune;
    }
#ifndef SQLITE4_OMIT_SUBQUERY
    case TK_SELECT:
Changes to src/sqlite.h.in.
4400
4401
4402
4403
4404
4405
4406







































































4407
4408
4409
4410
4411
4412
4413
      const char*, int, 
      int(*x)(void *ctx, int iWeight, int iOff, 
              const char *zToken, int nToken, int iSrc, int nSrc)
  ),
  int (*xDestroy)(sqlite4_tokenizer *)
);








































































/*
** Undo the hack that converts floating point types to integer for
** builds on processors without floating point support.
*/
#ifdef SQLITE4_OMIT_FLOATING_POINT
# undef double
#endif







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







4400
4401
4402
4403
4404
4405
4406
4407
4408
4409
4410
4411
4412
4413
4414
4415
4416
4417
4418
4419
4420
4421
4422
4423
4424
4425
4426
4427
4428
4429
4430
4431
4432
4433
4434
4435
4436
4437
4438
4439
4440
4441
4442
4443
4444
4445
4446
4447
4448
4449
4450
4451
4452
4453
4454
4455
4456
4457
4458
4459
4460
4461
4462
4463
4464
4465
4466
4467
4468
4469
4470
4471
4472
4473
4474
4475
4476
4477
4478
4479
4480
4481
4482
4483
4484
      const char*, int, 
      int(*x)(void *ctx, int iWeight, int iOff, 
              const char *zToken, int nToken, int iSrc, int nSrc)
  ),
  int (*xDestroy)(sqlite4_tokenizer *)
);

/*
** CAPI4REF: Register a matchinfo function.
*/
int sqlite4_create_mi_function(
  sqlite4 *db,
  const char *zFunc,
  int nArg,
  int enc,
  void *p,
  void (*xFunc)(sqlite4_context*,int,sqlite4_value **),
  void (*xDestroy)(void *)
);

/*
** Special functions that may be called from within matchinfo UDFs. All
** return an SQLite error code - SQLITE4_OK if successful, or some other
** error code otherwise.
**
** sqlite4_mi_column_count():
**   Set *pnCol to the number of columns in the queried table.
**
** sqlite4_mi_column_size():
**   Set *pnToken to the number of tokens in the value stored in column iCol 
**   of the current row.
**
** sqlite4_mi_column_value():
**   Set *ppVal to point to an sqlite4_value object containing the value
**   read from column iCol of the current row. This object is valid until
**   the function callback returns.
**
** sqlite4_mi_phrase_count():
**   Set *pnPhrase to the number of phrases in the query.
**
** sqlite4_mi_match_count():
**   Set *pn to the number of occurences of phrase iPhrase in column iCol of
**   the current row.
**
** sqlite4_mi_total_match_count():
**   Set *pnMatch to the total number of occurrences of phrase iPhrase
**   in column iCol of all rows in the indexed table. Set *pnDoc to the
**   number of rows that contain at least one match for phrase iPhrase in
**   column iCol.
**
** sqlite4_mi_match_offset():
**   Set *piOff to the token offset of the iMatch'th instance of phrase
**   iPhrase in column iCol of the current row. If any parameter is out
**   of range (i.e. too large) it is not an error. In this case *piOff is 
**   set to -1 before returning.
**   
** sqlite4_mi_total_size():
**   Set *pnToken to the total number of tokens in column iCol of all rows
**   in the indexed table.
**
** sqlite4_mi_total_count():
**   Set *pnRow to the total number of rows in the indexed table.
*/
int sqlite4_mi_column_count(sqlite4_context *, int *pnCol);
int sqlite4_mi_column_size(sqlite4_context *, int iCol, int *pnToken);
int sqlite4_mi_column_value(sqlite4_context *, int iCol, sqlite4_value **ppVal);

int sqlite4_mi_phrase_count(sqlite4_context *, int *pnPhrase);
int sqlite4_mi_match_count(sqlite4_context *, int iCol, int iPhrase, int *pn);
int sqlite4_mi_match_offset(
    sqlite4_context *, int iCol, int iPhrase, int iMatch, int *piOff); 

int sqlite4_mi_total_match_count(
    sqlite4_context *, int iCol, int iPhrase, int *pnMatch, int *pnDoc);

int sqlite4_mi_total_size(sqlite4_context *, int iCol, int *pnToken);
int sqlite4_mi_total_count(sqlite4_context *, int *pnRow);

/*
** Undo the hack that converts floating point types to integer for
** builds on processors without floating point support.
*/
#ifdef SQLITE4_OMIT_FLOATING_POINT
# undef double
#endif
Changes to src/sqliteInt.h.
635
636
637
638
639
640
641

642
643
644
645
646
647
648
  FuncDef *pSameName;  /* Next with a different name but the same hash */
  void (*xFunc)(sqlite4_context*,int,sqlite4_value**); /* Regular function */
  void (*xStep)(sqlite4_context*,int,sqlite4_value**); /* Aggregate step */
  void (*xFinalize)(sqlite4_context*);                /* Aggregate finalizer */
  char *zName;         /* SQL name of the function. */
  FuncDef *pNextName;  /* Next function with a different name */
  FuncDestructor *pDestructor;   /* Reference counted destructor function */

};

/*
** A table of SQL functions.  
**
** The content is a linked list of FuncDef structures with branches.  When
** there are two or more FuncDef objects with the same name, they are 







>







635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
  FuncDef *pSameName;  /* Next with a different name but the same hash */
  void (*xFunc)(sqlite4_context*,int,sqlite4_value**); /* Regular function */
  void (*xStep)(sqlite4_context*,int,sqlite4_value**); /* Aggregate step */
  void (*xFinalize)(sqlite4_context*);                /* Aggregate finalizer */
  char *zName;         /* SQL name of the function. */
  FuncDef *pNextName;  /* Next function with a different name */
  FuncDestructor *pDestructor;   /* Reference counted destructor function */
  u8 bMatchinfo;       /* True for matchinfo function */
};

/*
** A table of SQL functions.  
**
** The content is a linked list of FuncDef structures with branches.  When
** there are two or more FuncDef objects with the same name, they are 
Changes to src/vdbe.c.
1285
1286
1287
1288
1289
1290
1291








1292
1293
1294
1295
1296
1297
1298
** to retrieve the collation sequence set by this opcode is not available
** publicly, only to user functions defined in func.c.
*/
case OP_CollSeq: {
  assert( pOp->p4type==P4_COLLSEQ );
  break;
}









/* Opcode: Function P1 P2 P3 P4 P5
**
** Invoke a user function (P4 is a pointer to a Function structure that
** defines the function) with P5 arguments taken from register P2 and
** successors.  The result of the function is stored in register P3.
** Register P3 must not be one of the function inputs.







>
>
>
>
>
>
>
>







1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
** to retrieve the collation sequence set by this opcode is not available
** publicly, only to user functions defined in func.c.
*/
case OP_CollSeq: {
  assert( pOp->p4type==P4_COLLSEQ );
  break;
}

/* Opcode: Mifunction P1
*/
case OP_Mifunction: {
  pc++;
  pOp++;
  /* fall through to OP_Function */
};

/* Opcode: Function P1 P2 P3 P4 P5
**
** Invoke a user function (P4 is a pointer to a Function structure that
** defines the function) with P5 arguments taken from register P2 and
** successors.  The result of the function is stored in register P3.
** Register P3 must not be one of the function inputs.
1340
1341
1342
1343
1344
1345
1346







1347
1348
1349
1350
1351
1352
1353
    ctx.pFunc = ctx.pVdbeFunc->pFunc;
  }

  ctx.s.flags = MEM_Null;
  ctx.s.db = db;
  ctx.s.xDel = 0;
  ctx.s.zMalloc = 0;








  /* The output cell may already have a buffer allocated. Move
  ** the pointer to ctx.s so in case the user-function can use
  ** the already allocated buffer instead of allocating a new one.
  */
  sqlite4VdbeMemMove(&ctx.s, pOut);
  MemSetTypeFlag(&ctx.s, MEM_Null);







>
>
>
>
>
>
>







1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
    ctx.pFunc = ctx.pVdbeFunc->pFunc;
  }

  ctx.s.flags = MEM_Null;
  ctx.s.db = db;
  ctx.s.xDel = 0;
  ctx.s.zMalloc = 0;
  if( pOp[-1].opcode==OP_Mifunction ){
    ctx.pFts = p->apCsr[pOp[-1].p1]->pFts;
    apVal++;
    n--;
  }else{
    ctx.pFts = 0;
  }

  /* The output cell may already have a buffer allocated. Move
  ** the pointer to ctx.s so in case the user-function can use
  ** the already allocated buffer instead of allocating a new one.
  */
  sqlite4VdbeMemMove(&ctx.s, pOut);
  MemSetTypeFlag(&ctx.s, MEM_Null);
Changes to src/vdbeInt.h.
237
238
239
240
241
242
243

244
245
246
247
248
249
250
struct sqlite4_context {
  FuncDef *pFunc;       /* Pointer to function information.  MUST BE FIRST */
  VdbeFunc *pVdbeFunc;  /* Auxilary data, if created. */
  Mem s;                /* The return value is stored here */
  Mem *pMem;            /* Memory cell used to store aggregate context */
  int isError;          /* Error code returned by the function. */
  CollSeq *pColl;       /* Collating sequence */

};

/*
** An Explain object accumulates indented output which is helpful
** in describing recursive data structures.
*/
struct Explain {







>







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

/*
** An Explain object accumulates indented output which is helpful
** in describing recursive data structures.
*/
struct Explain {
Changes to src/where.c.
5225
5226
5227
5228
5229
5230
5231


















5232
5233
5234
5235
5236
5237
5238
      while( pOp<pEnd ){
        if( pOp->p1==pLevel->iTabCur && pOp->opcode==OP_Column ){
          pOp->p1 = pLevel->iIdxCur;
        }
        pOp++;
      }
    }


















  }

  /* Final cleanup
  */
  pParse->nQueryLoop = pWInfo->savedNQueryLoop;
  whereInfoFree(db, pWInfo);
  return;







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







5225
5226
5227
5228
5229
5230
5231
5232
5233
5234
5235
5236
5237
5238
5239
5240
5241
5242
5243
5244
5245
5246
5247
5248
5249
5250
5251
5252
5253
5254
5255
5256
      while( pOp<pEnd ){
        if( pOp->p1==pLevel->iTabCur && pOp->opcode==OP_Column ){
          pOp->p1 = pLevel->iIdxCur;
        }
        pOp++;
      }
    }

    if( (pLevel->plan.wsFlags & WHERE_INDEXED)
     && (pLevel->plan.u.pIdx->eIndexType==SQLITE4_INDEX_FTS5)
    ){
      VdbeOp *pOp;
      VdbeOp *pEnd;

      assert( pLevel->iTabCur!=pLevel->iIdxCur );
      pOp = sqlite4VdbeGetOp(v, pWInfo->iTop);
      pEnd = &pOp[sqlite4VdbeCurrentAddr(v) - pWInfo->iTop];

      while( pOp<pEnd ){
        if( pOp->p1==pLevel->iTabCur && pOp->opcode==OP_Mifunction ){
          pOp->p1 = pLevel->iIdxCur;
        }
        pOp++;
      }
    }
  }

  /* Final cleanup
  */
  pParse->nQueryLoop = pWInfo->savedNQueryLoop;
  whereInfoFree(db, pWInfo);
  return;
Changes to test/fts5create.test.
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
  CREATE INDEX ft ON t2 USING fts5(tukenizer=simple);
} {1 {unrecognized argument: "tukenizer"}}

do_catchsql_test 2.3 { 
  CREATE INDEX ft ON t2 USING fts5("a b c");
} {1 {unrecognized argument: "a b c"}}

do_catchsql_test 2.4 { 
  CREATE INDEX ft ON t2 USING fts5(tokenizer="nosuch");
} {1 {no such tokenizer: "nosuch"}}

finish_test










|







70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
  CREATE INDEX ft ON t2 USING fts5(tukenizer=simple);
} {1 {unrecognized argument: "tukenizer"}}

do_catchsql_test 2.3 { 
  CREATE INDEX ft ON t2 USING fts5("a b c");
} {1 {unrecognized argument: "a b c"}}

do_catchsql_test 2.4 {
  CREATE INDEX ft ON t2 USING fts5(tokenizer="nosuch");
} {1 {no such tokenizer: "nosuch"}}

finish_test



Changes to test/fts5query1.test.
130
131
132
133
134
135
136













137
138
139
  2 {a:a}  {1}
  3 {b:a}  {2}
  4 {c:a}  {1 2}
  5 {a:a*} {1}
} {
  do_execsql_test 7.$tn {SELECT docid FROM t7 WHERE t7 MATCH $expr} $res
}














finish_test








>
>
>
>
>
>
>
>
>
>
>
>
>



130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
  2 {a:a}  {1}
  3 {b:a}  {2}
  4 {c:a}  {1 2}
  5 {a:a*} {1}
} {
  do_execsql_test 7.$tn {SELECT docid FROM t7 WHERE t7 MATCH $expr} $res
}

#-------------------------------------------------------------------------
#
do_execsql_test 8.0 {
  CREATE TABLE t8(a PRIMARY KEY, b, c);
  CREATE INDEX i8 ON t8 USING fts5();
  INSERT INTO t8 VALUES('one', 'a b c', 'a a a');
  INSERT INTO t8 VALUES('two', 'd e f', 'b b b');
}

do_execsql_test 8.1 {
  SELECT rank(t8) FROM t8 WHERE t8 MATCH 'b a'
}

finish_test