/ Check-in [dddd7e1829]
Login

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

Overview
Comment:Optimizations for the matchinfo() function, particularly the 'y' flag.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | fts3-matchinfo-y
Files: files | file ages | folders
SHA1: dddd7e182943a1d3a9d32830e819a63f1a228d6d
User & Date: dan 2015-05-05 19:37:07
Context
2015-05-05
20:39
Add the fts3 matchinfo 'b' flag. check-in: b9b77972d8 user: dan tags: fts3-matchinfo-y
19:37
Optimizations for the matchinfo() function, particularly the 'y' flag. check-in: dddd7e1829 user: dan tags: fts3-matchinfo-y
11:08
Add #ifdef statements to test_blob.c so that it will build with SQLITE_OMIT_INCRBLOB. check-in: b8f090e65d user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to ext/fts3/fts3.c.

  1671   1671   static int fts3CloseMethod(sqlite3_vtab_cursor *pCursor){
  1672   1672     Fts3Cursor *pCsr = (Fts3Cursor *)pCursor;
  1673   1673     assert( ((Fts3Table *)pCsr->base.pVtab)->pSegments==0 );
  1674   1674     sqlite3_finalize(pCsr->pStmt);
  1675   1675     sqlite3Fts3ExprFree(pCsr->pExpr);
  1676   1676     sqlite3Fts3FreeDeferredTokens(pCsr);
  1677   1677     sqlite3_free(pCsr->aDoclist);
  1678         -  sqlite3_free(pCsr->aMatchinfo);
         1678  +  sqlite3Fts3MIBufferFree(pCsr->pMIBuffer);
  1679   1679     assert( ((Fts3Table *)pCsr->base.pVtab)->pSegments==0 );
  1680   1680     sqlite3_free(pCsr);
  1681   1681     return SQLITE_OK;
  1682   1682   }
  1683   1683   
  1684   1684   /*
  1685   1685   ** If pCsr->pStmt has not been prepared (i.e. if pCsr->pStmt==0), then
................................................................................
  3172   3172     if( idxNum & FTS3_HAVE_DOCID_GE ) pDocidGe = apVal[iIdx++];
  3173   3173     if( idxNum & FTS3_HAVE_DOCID_LE ) pDocidLe = apVal[iIdx++];
  3174   3174     assert( iIdx==nVal );
  3175   3175   
  3176   3176     /* In case the cursor has been used before, clear it now. */
  3177   3177     sqlite3_finalize(pCsr->pStmt);
  3178   3178     sqlite3_free(pCsr->aDoclist);
  3179         -  sqlite3_free(pCsr->aMatchinfo);
         3179  +  sqlite3Fts3MIBufferFree(pCsr->pMIBuffer);
  3180   3180     sqlite3Fts3ExprFree(pCsr->pExpr);
  3181   3181     memset(&pCursor[1], 0, sizeof(Fts3Cursor)-sizeof(sqlite3_vtab_cursor));
  3182   3182   
  3183   3183     /* Set the lower and upper bounds on docids to return */
  3184   3184     pCsr->iMinDocid = fts3DocidRange(pDocidGe, SMALLEST_INT64);
  3185   3185     pCsr->iMaxDocid = fts3DocidRange(pDocidLe, LARGEST_INT64);
  3186   3186   

Changes to ext/fts3/fts3Int.h.

   193    193   
   194    194   typedef struct Fts3Doclist Fts3Doclist;
   195    195   typedef struct Fts3SegFilter Fts3SegFilter;
   196    196   typedef struct Fts3DeferredToken Fts3DeferredToken;
   197    197   typedef struct Fts3SegReader Fts3SegReader;
   198    198   typedef struct Fts3MultiSegReader Fts3MultiSegReader;
   199    199   
          200  +typedef struct MatchinfoBuffer MatchinfoBuffer;
          201  +
   200    202   /*
   201    203   ** A connection to a fulltext index is an instance of the following
   202    204   ** structure. The xCreate and xConnect methods create an instance
   203    205   ** of this structure and xDestroy and xDisconnect free that instance.
   204    206   ** All other methods receive a pointer to the structure as one of their
   205    207   ** arguments.
   206    208   */
................................................................................
   302    304     u8 bDesc;                       /* True to sort in descending order */
   303    305     int eEvalmode;                  /* An FTS3_EVAL_XX constant */
   304    306     int nRowAvg;                    /* Average size of database rows, in pages */
   305    307     sqlite3_int64 nDoc;             /* Documents in table */
   306    308     i64 iMinDocid;                  /* Minimum docid to return */
   307    309     i64 iMaxDocid;                  /* Maximum docid to return */
   308    310     int isMatchinfoNeeded;          /* True when aMatchinfo[] needs filling in */
   309         -  u32 *aMatchinfo;                /* Information about most recent match */
   310         -  int nMatchinfo;                 /* Number of elements in aMatchinfo[] */
   311         -  char *zMatchinfo;               /* Matchinfo specification */
          311  +  MatchinfoBuffer *pMIBuffer;     /* Buffer for matchinfo data */
   312    312   };
   313    313   
   314    314   #define FTS3_EVAL_FILTER    0
   315    315   #define FTS3_EVAL_NEXT      1
   316    316   #define FTS3_EVAL_MATCHINFO 2
   317    317   
   318    318   /*
................................................................................
   560    560   
   561    561   /* fts3_snippet.c */
   562    562   void sqlite3Fts3Offsets(sqlite3_context*, Fts3Cursor*);
   563    563   void sqlite3Fts3Snippet(sqlite3_context *, Fts3Cursor *, const char *,
   564    564     const char *, const char *, int, int
   565    565   );
   566    566   void sqlite3Fts3Matchinfo(sqlite3_context *, Fts3Cursor *, const char *);
          567  +void sqlite3Fts3MIBufferFree(MatchinfoBuffer *p);
   567    568   
   568    569   /* fts3_expr.c */
   569    570   int sqlite3Fts3ExprParse(sqlite3_tokenizer *, int,
   570    571     char **, int, int, int, const char *, int, Fts3Expr **, char **
   571    572   );
   572    573   void sqlite3Fts3ExprFree(Fts3Expr *);
   573    574   #ifdef SQLITE_TEST

Changes to ext/fts3/fts3_snippet.c.

    88     88     Fts3Cursor *pCursor;            /* FTS3 Cursor */
    89     89     int nCol;                       /* Number of columns in table */
    90     90     int nPhrase;                    /* Number of matchable phrases in query */
    91     91     sqlite3_int64 nDoc;             /* Number of docs in database */
    92     92     u32 *aMatchinfo;                /* Pre-allocated buffer */
    93     93   };
    94     94   
           95  +/*
           96  +** An instance of this structure is used to manage a pair of buffers, each
           97  +** (nElem * sizeof(u32)) bytes in size. See the MatchinfoBuffer code below
           98  +** for details.
           99  +*/
          100  +struct MatchinfoBuffer {
          101  +  u8 aRef[3];
          102  +  int nElem;
          103  +  int bGlobal;                    /* Set if global data is loaded */
          104  +  char *zMatchinfo;
          105  +  u32 aMatchinfo[0];
          106  +};
    95    107   
    96    108   
    97    109   /*
    98    110   ** The snippet() and offsets() functions both return text values. An instance
    99    111   ** of the following structure is used to accumulate those values while the
   100    112   ** functions are running. See fts3StringAppend() for details.
   101    113   */
................................................................................
   102    114   typedef struct StrBuffer StrBuffer;
   103    115   struct StrBuffer {
   104    116     char *z;                        /* Pointer to buffer containing string */
   105    117     int n;                          /* Length of z in bytes (excl. nul-term) */
   106    118     int nAlloc;                     /* Allocated size of buffer z in bytes */
   107    119   };
   108    120   
          121  +
          122  +/*************************************************************************
          123  +** Start of MatchinfoBuffer code.
          124  +*/
          125  +
          126  +/*
          127  +** Allocate a two-slot MatchinfoBuffer object.
          128  +*/
          129  +static MatchinfoBuffer *fts3MIBufferNew(int nElem, const char *zMatchinfo){
          130  +  MatchinfoBuffer *pRet;
          131  +  int nByte = sizeof(u32) * (2*nElem + 2) + sizeof(MatchinfoBuffer);
          132  +  int nStr = strlen(zMatchinfo);
          133  +
          134  +  pRet = sqlite3_malloc(nByte + nStr+1);
          135  +  if( pRet ){
          136  +    memset(pRet, 0, nByte);
          137  +    pRet->aMatchinfo[0] = (u8*)(&pRet->aMatchinfo[1]) - (u8*)pRet;
          138  +    pRet->aMatchinfo[1+nElem] = pRet->aMatchinfo[0] + sizeof(u32)*(nElem+1);
          139  +    pRet->nElem = nElem;
          140  +    pRet->zMatchinfo = ((char*)pRet) + nByte;
          141  +    memcpy(pRet->zMatchinfo, zMatchinfo, nStr+1);
          142  +    pRet->aRef[0] = 1;
          143  +  }
          144  +
          145  +  return pRet;
          146  +}
          147  +
          148  +static void fts3MIBufferFree(void *p){
          149  +  MatchinfoBuffer *pBuf = (MatchinfoBuffer*)((u8*)p - ((u32*)p)[-1]);
          150  +
          151  +  assert( (u32*)p==&pBuf->aMatchinfo[1] 
          152  +       || (u32*)p==&pBuf->aMatchinfo[pBuf->nElem+2] 
          153  +  );
          154  +  if( (u32*)p==&pBuf->aMatchinfo[1] ){
          155  +    pBuf->aRef[1] = 0;
          156  +  }else{
          157  +    pBuf->aRef[2] = 0;
          158  +  }
          159  +
          160  +  if( pBuf->aRef[0]==0 && pBuf->aRef[1]==0 && pBuf->aRef[2]==0 ){
          161  +    sqlite3_free(pBuf);
          162  +  }
          163  +}
          164  +
          165  +static void (*fts3MIBufferAlloc(MatchinfoBuffer *p, u32 **paOut))(void*){
          166  +  void (*xRet)(void*) = 0;
          167  +  u32 *aOut = 0;
          168  +
          169  +  if( p->aRef[1]==0 ){
          170  +    p->aRef[1] = 1;
          171  +    aOut = &p->aMatchinfo[1];
          172  +    xRet = fts3MIBufferFree;
          173  +  }
          174  +  else if( p->aRef[2]==0 ){
          175  +    p->aRef[2] = 1;
          176  +    aOut = &p->aMatchinfo[p->nElem+2];
          177  +    xRet = fts3MIBufferFree;
          178  +  }else{
          179  +    aOut = (u32*)sqlite3_malloc(p->nElem * sizeof(u32));
          180  +    if( aOut ){
          181  +      xRet = sqlite3_free;
          182  +      if( p->bGlobal ) memcpy(aOut, &p->aMatchinfo[1], p->nElem*sizeof(u32));
          183  +    }
          184  +  }
          185  +
          186  +  *paOut = aOut;
          187  +  return xRet;
          188  +}
          189  +
          190  +static void fts3MIBufferSetGlobal(MatchinfoBuffer *p){
          191  +  p->bGlobal = 1;
          192  +  memcpy(&p->aMatchinfo[2+p->nElem], &p->aMatchinfo[1], p->nElem*sizeof(u32));
          193  +}
          194  +
          195  +/*
          196  +** Free a MatchinfoBuffer object allocated using fts3MIBufferNew()
          197  +*/
          198  +void sqlite3Fts3MIBufferFree(MatchinfoBuffer *p){
          199  +  if( p ){
          200  +    assert( p->aRef[0]==1 );
          201  +    p->aRef[0] = 0;
          202  +    if( p->aRef[0]==0 && p->aRef[1]==0 && p->aRef[2]==0 ){
          203  +      sqlite3_free(p);
          204  +    }
          205  +  }
          206  +}
          207  +
          208  +/* 
          209  +** End of MatchinfoBuffer code.
          210  +*************************************************************************/
          211  +
   109    212   
   110    213   /*
   111    214   ** This function is used to help iterate through a position-list. A position
   112    215   ** list is a list of unique integers, sorted from smallest to largest. Each
   113    216   ** element of the list is represented by an FTS3 varint that takes the value
   114    217   ** of the difference between the current element and the previous one plus
   115    218   ** two. For example, to store the position-list:
................................................................................
   129    232   */
   130    233   static void fts3GetDeltaPosition(char **pp, int *piPos){
   131    234     int iVal;
   132    235     *pp += fts3GetVarint32(*pp, &iVal);
   133    236     *piPos += (iVal-2);
   134    237   }
   135    238   
          239  +static int fts3ExprLHitsCb(Fts3Expr*,int,void*);
          240  +
   136    241   /*
   137    242   ** Helper function for fts3ExprIterate() (see below).
   138    243   */
   139    244   static int fts3ExprIterate2(
   140    245     Fts3Expr *pExpr,                /* Expression to iterate phrases of */
   141    246     int *piPhrase,                  /* Pointer to phrase counter */
   142    247     int (*x)(Fts3Expr*,int,void*),  /* Callback function to invoke for phrases */
   143    248     void *pCtx                      /* Second argument to pass to callback */
   144    249   ){
   145    250     int rc;                         /* Return code */
   146         -  int eType = pExpr->eType;       /* Type of expression node pExpr */
   147    251   
   148         -  if( eType!=FTSQUERY_PHRASE ){
   149         -    assert( pExpr->pLeft && pExpr->pRight );
   150         -    rc = fts3ExprIterate2(pExpr->pLeft, piPhrase, x, pCtx);
   151         -    if( rc==SQLITE_OK && eType!=FTSQUERY_NOT ){
   152         -      rc = fts3ExprIterate2(pExpr->pRight, piPhrase, x, pCtx);
   153         -    }
          252  +  if( x==fts3ExprLHitsCb && pExpr->bEof ){
          253  +    rc = SQLITE_OK;
   154    254     }else{
   155         -    rc = x(pExpr, *piPhrase, pCtx);
   156         -    (*piPhrase)++;
          255  +    int eType = pExpr->eType;     /* Type of expression node pExpr */
          256  +    if( eType!=FTSQUERY_PHRASE ){
          257  +      assert( pExpr->pLeft && pExpr->pRight );
          258  +      rc = fts3ExprIterate2(pExpr->pLeft, piPhrase, x, pCtx);
          259  +      if( rc==SQLITE_OK && eType!=FTSQUERY_NOT ){
          260  +        rc = fts3ExprIterate2(pExpr->pRight, piPhrase, x, pCtx);
          261  +      }
          262  +    }else{
          263  +      rc = x(pExpr, *piPhrase, pCtx);
          264  +      (*piPhrase)++;
          265  +    }
   157    266     }
   158    267     return rc;
   159    268   }
   160    269   
   161    270   /*
   162    271   ** Iterate through all phrase nodes in an FTS3 query, except those that
   163    272   ** are part of a sub-tree that is the right-hand-side of a NOT operator.
................................................................................
   815    924   ** directive 'y'.
   816    925   */
   817    926   static int fts3ExprLHitsCb(
   818    927     Fts3Expr *pExpr,                /* Phrase expression node */
   819    928     int iPhrase,                    /* Phrase number */
   820    929     void *pCtx                      /* Pointer to MatchInfo structure */
   821    930   ){
   822         -  MatchInfo *p = (MatchInfo *)pCtx;
   823         -  Fts3Table *pTab = (Fts3Table *)p->pCursor->base.pVtab;
   824    931     int rc = SQLITE_OK;
   825         -  int iStart = iPhrase * p->nCol;
   826         -  Fts3Expr *pEof;                 /* Ancestor node already at EOF */
          932  +  MatchInfo *p = (MatchInfo *)pCtx;
   827    933     
   828    934     /* This must be a phrase */
   829    935     assert( pExpr->pPhrase );
   830    936   
   831         -  /* Initialize all output integers to zero. */
   832         -  memset(&p->aMatchinfo[iStart], 0, sizeof(u32) * p->nCol);
   833         -
   834         -  /* Check if this or any parent node is at EOF. If so, then all output
   835         -  ** values are zero.  */
   836         -  for(pEof=pExpr; pEof && pEof->bEof==0; pEof=pEof->pParent);
   837         -
   838         -  if( pEof==0 && pExpr->iDocid==p->pCursor->iPrevId ){
          937  +  if( pExpr->iDocid==p->pCursor->iPrevId ){
          938  +    Fts3Table *pTab = (Fts3Table *)p->pCursor->base.pVtab;
          939  +    int iStart = iPhrase * p->nCol;
   839    940       Fts3Phrase *pPhrase = pExpr->pPhrase;
   840    941       char *pIter = pPhrase->doclist.pList;
   841    942       int iCol = 0;
   842    943   
   843    944       while( 1 ){
   844    945         int nHit = fts3ColumnlistCount(&pIter);
   845    946         if( (pPhrase->iColumn>=pTab->nColumn || pPhrase->iColumn==iCol) ){
................................................................................
  1145   1246         case FTS3_MATCHINFO_LCS:
  1146   1247           rc = fts3ExprLoadDoclists(pCsr, 0, 0);
  1147   1248           if( rc==SQLITE_OK ){
  1148   1249             rc = fts3MatchinfoLcs(pCsr, pInfo);
  1149   1250           }
  1150   1251           break;
  1151   1252   
  1152         -      case FTS3_MATCHINFO_LHITS:
         1253  +      case FTS3_MATCHINFO_LHITS: {
         1254  +        int nZero = fts3MatchinfoSize(pInfo, FTS3_MATCHINFO_LHITS)*sizeof(u32);
         1255  +        memset(pInfo->aMatchinfo, 0, nZero);
  1153   1256           (void)fts3ExprIterate(pCsr->pExpr, fts3ExprLHitsCb, (void*)pInfo);
  1154   1257           break;
         1258  +      }
  1155   1259   
  1156   1260         default: {
  1157   1261           Fts3Expr *pExpr;
  1158   1262           assert( zArg[i]==FTS3_MATCHINFO_HITS );
  1159   1263           pExpr = pCsr->pExpr;
  1160   1264           rc = fts3ExprLoadDoclists(pCsr, 0, 0);
  1161   1265           if( rc!=SQLITE_OK ) break;
................................................................................
  1181   1285   
  1182   1286   
  1183   1287   /*
  1184   1288   ** Populate pCsr->aMatchinfo[] with data for the current row. The 
  1185   1289   ** 'matchinfo' data is an array of 32-bit unsigned integers (C type u32).
  1186   1290   */
  1187   1291   static int fts3GetMatchinfo(
         1292  +  sqlite3_context *pCtx,        /* Return results here */
  1188   1293     Fts3Cursor *pCsr,               /* FTS3 Cursor object */
  1189   1294     const char *zArg                /* Second argument to matchinfo() function */
  1190   1295   ){
  1191   1296     MatchInfo sInfo;
  1192   1297     Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab;
  1193   1298     int rc = SQLITE_OK;
  1194   1299     int bGlobal = 0;                /* Collect 'global' stats as well as local */
  1195   1300   
         1301  +  u32 *aOut = 0;
         1302  +  void (*xDestroyOut)(void*) = 0;
         1303  +
  1196   1304     memset(&sInfo, 0, sizeof(MatchInfo));
  1197   1305     sInfo.pCursor = pCsr;
  1198   1306     sInfo.nCol = pTab->nColumn;
  1199   1307   
  1200   1308     /* If there is cached matchinfo() data, but the format string for the 
  1201   1309     ** cache does not match the format string for this request, discard 
  1202   1310     ** the cached data. */
  1203         -  if( pCsr->zMatchinfo && strcmp(pCsr->zMatchinfo, zArg) ){
  1204         -    assert( pCsr->aMatchinfo );
  1205         -    sqlite3_free(pCsr->aMatchinfo);
  1206         -    pCsr->zMatchinfo = 0;
  1207         -    pCsr->aMatchinfo = 0;
         1311  +  if( pCsr->pMIBuffer && strcmp(pCsr->pMIBuffer->zMatchinfo, zArg) ){
         1312  +    sqlite3Fts3MIBufferFree(pCsr->pMIBuffer);
         1313  +    pCsr->pMIBuffer = 0;
  1208   1314     }
  1209   1315   
  1210         -  /* If Fts3Cursor.aMatchinfo[] is NULL, then this is the first time the
         1316  +  /* If Fts3Cursor.pMIBuffer is NULL, then this is the first time the
  1211   1317     ** matchinfo function has been called for this query. In this case 
  1212   1318     ** allocate the array used to accumulate the matchinfo data and
  1213   1319     ** initialize those elements that are constant for every row.
  1214   1320     */
  1215         -  if( pCsr->aMatchinfo==0 ){
         1321  +  if( pCsr->pMIBuffer==0 ){
  1216   1322       int nMatchinfo = 0;           /* Number of u32 elements in match-info */
  1217   1323       int nArg;                     /* Bytes in zArg */
  1218   1324       int i;                        /* Used to iterate through zArg */
  1219   1325   
  1220   1326       /* Determine the number of phrases in the query */
  1221   1327       pCsr->nPhrase = fts3ExprPhraseCount(pCsr->pExpr);
  1222   1328       sInfo.nPhrase = pCsr->nPhrase;
................................................................................
  1223   1329   
  1224   1330       /* Determine the number of integers in the buffer returned by this call. */
  1225   1331       for(i=0; zArg[i]; i++){
  1226   1332         nMatchinfo += fts3MatchinfoSize(&sInfo, zArg[i]);
  1227   1333       }
  1228   1334   
  1229   1335       /* Allocate space for Fts3Cursor.aMatchinfo[] and Fts3Cursor.zMatchinfo. */
  1230         -    nArg = (int)strlen(zArg);
  1231         -    pCsr->aMatchinfo = (u32 *)sqlite3_malloc(sizeof(u32)*nMatchinfo + nArg + 1);
  1232         -    if( !pCsr->aMatchinfo ) return SQLITE_NOMEM;
         1336  +    pCsr->pMIBuffer = fts3MIBufferNew(nMatchinfo, zArg);
         1337  +    if( !pCsr->pMIBuffer ) rc = SQLITE_NOMEM;
  1233   1338   
  1234         -    pCsr->zMatchinfo = (char *)&pCsr->aMatchinfo[nMatchinfo];
  1235         -    pCsr->nMatchinfo = nMatchinfo;
  1236         -    memcpy(pCsr->zMatchinfo, zArg, nArg+1);
  1237         -    memset(pCsr->aMatchinfo, 0, sizeof(u32)*nMatchinfo);
  1238   1339       pCsr->isMatchinfoNeeded = 1;
  1239   1340       bGlobal = 1;
  1240   1341     }
  1241   1342   
  1242         -  sInfo.aMatchinfo = pCsr->aMatchinfo;
  1243         -  sInfo.nPhrase = pCsr->nPhrase;
  1244         -  if( pCsr->isMatchinfoNeeded ){
         1343  +  if( rc==SQLITE_OK ){
         1344  +    xDestroyOut = fts3MIBufferAlloc(pCsr->pMIBuffer, &aOut);
         1345  +    if( xDestroyOut==0 ){
         1346  +      rc = SQLITE_NOMEM;
         1347  +    }
         1348  +  }
         1349  +
         1350  +  if( rc==SQLITE_OK ){
         1351  +    sInfo.aMatchinfo = aOut;
         1352  +    sInfo.nPhrase = pCsr->nPhrase;
  1245   1353       rc = fts3MatchinfoValues(pCsr, bGlobal, &sInfo, zArg);
  1246         -    pCsr->isMatchinfoNeeded = 0;
         1354  +    if( bGlobal ){
         1355  +      fts3MIBufferSetGlobal(pCsr->pMIBuffer);
         1356  +    }
         1357  +  }
         1358  +
         1359  +  if( rc!=SQLITE_OK ){
         1360  +    sqlite3_result_error_code(pCtx, rc);
         1361  +    if( xDestroyOut ) xDestroyOut(aOut);
         1362  +  }else{
         1363  +    int n = pCsr->pMIBuffer->nElem * sizeof(u32);
         1364  +    sqlite3_result_blob(pCtx, aOut, n, xDestroyOut);
  1247   1365     }
  1248   1366   
  1249   1367     return rc;
  1250   1368   }
  1251   1369   
  1252   1370   /*
  1253   1371   ** Implementation of snippet() function.
................................................................................
  1564   1682   
  1565   1683     if( !pCsr->pExpr ){
  1566   1684       sqlite3_result_blob(pContext, "", 0, SQLITE_STATIC);
  1567   1685       return;
  1568   1686     }
  1569   1687   
  1570   1688     /* Retrieve matchinfo() data. */
  1571         -  rc = fts3GetMatchinfo(pCsr, zFormat);
         1689  +  rc = fts3GetMatchinfo(pContext, pCsr, zFormat);
  1572   1690     sqlite3Fts3SegmentsClose(pTab);
  1573         -
  1574         -  if( rc!=SQLITE_OK ){
  1575         -    sqlite3_result_error_code(pContext, rc);
  1576         -  }else{
  1577         -    int n = pCsr->nMatchinfo * sizeof(u32);
  1578         -    sqlite3_result_blob(pContext, pCsr->aMatchinfo, n, SQLITE_TRANSIENT);
  1579         -  }
  1580   1691   }
  1581   1692   
  1582   1693   #endif