modernc.org/cc@v1.0.1/v2/testdata/_sqlite/ext/fts5/fts5_vocab.c (about)

     1  /*
     2  ** 2015 May 08
     3  **
     4  ** The author disclaims copyright to this source code.  In place of
     5  ** a legal notice, here is a blessing:
     6  **
     7  **    May you do good and not evil.
     8  **    May you find forgiveness for yourself and forgive others.
     9  **    May you share freely, never taking more than you give.
    10  **
    11  ******************************************************************************
    12  **
    13  ** This is an SQLite virtual table module implementing direct access to an
    14  ** existing FTS5 index. The module may create several different types of 
    15  ** tables:
    16  **
    17  ** col:
    18  **     CREATE TABLE vocab(term, col, doc, cnt, PRIMARY KEY(term, col));
    19  **
    20  **   One row for each term/column combination. The value of $doc is set to
    21  **   the number of fts5 rows that contain at least one instance of term
    22  **   $term within column $col. Field $cnt is set to the total number of 
    23  **   instances of term $term in column $col (in any row of the fts5 table). 
    24  **
    25  ** row:
    26  **     CREATE TABLE vocab(term, doc, cnt, PRIMARY KEY(term));
    27  **
    28  **   One row for each term in the database. The value of $doc is set to
    29  **   the number of fts5 rows that contain at least one instance of term
    30  **   $term. Field $cnt is set to the total number of instances of term 
    31  **   $term in the database.
    32  **
    33  ** instance:
    34  **     CREATE TABLE vocab(term, doc, col, offset, PRIMARY KEY(<all-fields>));
    35  **
    36  **   One row for each term instance in the database. 
    37  */
    38  
    39  
    40  #include "fts5Int.h"
    41  
    42  
    43  typedef struct Fts5VocabTable Fts5VocabTable;
    44  typedef struct Fts5VocabCursor Fts5VocabCursor;
    45  
    46  struct Fts5VocabTable {
    47    sqlite3_vtab base;
    48    char *zFts5Tbl;                 /* Name of fts5 table */
    49    char *zFts5Db;                  /* Db containing fts5 table */
    50    sqlite3 *db;                    /* Database handle */
    51    Fts5Global *pGlobal;            /* FTS5 global object for this database */
    52    int eType;                      /* FTS5_VOCAB_COL, ROW or INSTANCE */
    53  };
    54  
    55  struct Fts5VocabCursor {
    56    sqlite3_vtab_cursor base;
    57    sqlite3_stmt *pStmt;            /* Statement holding lock on pIndex */
    58    Fts5Index *pIndex;              /* Associated FTS5 index */
    59  
    60    int bEof;                       /* True if this cursor is at EOF */
    61    Fts5IndexIter *pIter;           /* Term/rowid iterator object */
    62  
    63    int nLeTerm;                    /* Size of zLeTerm in bytes */
    64    char *zLeTerm;                  /* (term <= $zLeTerm) paramater, or NULL */
    65  
    66    /* These are used by 'col' tables only */
    67    Fts5Config *pConfig;            /* Fts5 table configuration */
    68    int iCol;
    69    i64 *aCnt;
    70    i64 *aDoc;
    71  
    72    /* Output values used by all tables. */
    73    i64 rowid;                      /* This table's current rowid value */
    74    Fts5Buffer term;                /* Current value of 'term' column */
    75  
    76    /* Output values Used by 'instance' tables only */
    77    i64 iInstPos;
    78    int iInstOff;
    79  };
    80  
    81  #define FTS5_VOCAB_COL      0
    82  #define FTS5_VOCAB_ROW      1
    83  #define FTS5_VOCAB_INSTANCE 2
    84  
    85  #define FTS5_VOCAB_COL_SCHEMA  "term, col, doc, cnt"
    86  #define FTS5_VOCAB_ROW_SCHEMA  "term, doc, cnt"
    87  #define FTS5_VOCAB_INST_SCHEMA "term, doc, col, offset"
    88  
    89  /*
    90  ** Bits for the mask used as the idxNum value by xBestIndex/xFilter.
    91  */
    92  #define FTS5_VOCAB_TERM_EQ 0x01
    93  #define FTS5_VOCAB_TERM_GE 0x02
    94  #define FTS5_VOCAB_TERM_LE 0x04
    95  
    96  
    97  /*
    98  ** Translate a string containing an fts5vocab table type to an 
    99  ** FTS5_VOCAB_XXX constant. If successful, set *peType to the output
   100  ** value and return SQLITE_OK. Otherwise, set *pzErr to an error message
   101  ** and return SQLITE_ERROR.
   102  */
   103  static int fts5VocabTableType(const char *zType, char **pzErr, int *peType){
   104    int rc = SQLITE_OK;
   105    char *zCopy = sqlite3Fts5Strndup(&rc, zType, -1);
   106    if( rc==SQLITE_OK ){
   107      sqlite3Fts5Dequote(zCopy);
   108      if( sqlite3_stricmp(zCopy, "col")==0 ){
   109        *peType = FTS5_VOCAB_COL;
   110      }else
   111  
   112      if( sqlite3_stricmp(zCopy, "row")==0 ){
   113        *peType = FTS5_VOCAB_ROW;
   114      }else
   115      if( sqlite3_stricmp(zCopy, "instance")==0 ){
   116        *peType = FTS5_VOCAB_INSTANCE;
   117      }else
   118      {
   119        *pzErr = sqlite3_mprintf("fts5vocab: unknown table type: %Q", zCopy);
   120        rc = SQLITE_ERROR;
   121      }
   122      sqlite3_free(zCopy);
   123    }
   124  
   125    return rc;
   126  }
   127  
   128  
   129  /*
   130  ** The xDisconnect() virtual table method.
   131  */
   132  static int fts5VocabDisconnectMethod(sqlite3_vtab *pVtab){
   133    Fts5VocabTable *pTab = (Fts5VocabTable*)pVtab;
   134    sqlite3_free(pTab);
   135    return SQLITE_OK;
   136  }
   137  
   138  /*
   139  ** The xDestroy() virtual table method.
   140  */
   141  static int fts5VocabDestroyMethod(sqlite3_vtab *pVtab){
   142    Fts5VocabTable *pTab = (Fts5VocabTable*)pVtab;
   143    sqlite3_free(pTab);
   144    return SQLITE_OK;
   145  }
   146  
   147  /*
   148  ** This function is the implementation of both the xConnect and xCreate
   149  ** methods of the FTS3 virtual table.
   150  **
   151  ** The argv[] array contains the following:
   152  **
   153  **   argv[0]   -> module name  ("fts5vocab")
   154  **   argv[1]   -> database name
   155  **   argv[2]   -> table name
   156  **
   157  ** then:
   158  **
   159  **   argv[3]   -> name of fts5 table
   160  **   argv[4]   -> type of fts5vocab table
   161  **
   162  ** or, for tables in the TEMP schema only.
   163  **
   164  **   argv[3]   -> name of fts5 tables database
   165  **   argv[4]   -> name of fts5 table
   166  **   argv[5]   -> type of fts5vocab table
   167  */
   168  static int fts5VocabInitVtab(
   169    sqlite3 *db,                    /* The SQLite database connection */
   170    void *pAux,                     /* Pointer to Fts5Global object */
   171    int argc,                       /* Number of elements in argv array */
   172    const char * const *argv,       /* xCreate/xConnect argument array */
   173    sqlite3_vtab **ppVTab,          /* Write the resulting vtab structure here */
   174    char **pzErr                    /* Write any error message here */
   175  ){
   176    const char *azSchema[] = { 
   177      "CREATE TABlE vocab(" FTS5_VOCAB_COL_SCHEMA  ")", 
   178      "CREATE TABlE vocab(" FTS5_VOCAB_ROW_SCHEMA  ")",
   179      "CREATE TABlE vocab(" FTS5_VOCAB_INST_SCHEMA ")"
   180    };
   181  
   182    Fts5VocabTable *pRet = 0;
   183    int rc = SQLITE_OK;             /* Return code */
   184    int bDb;
   185  
   186    bDb = (argc==6 && strlen(argv[1])==4 && memcmp("temp", argv[1], 4)==0);
   187  
   188    if( argc!=5 && bDb==0 ){
   189      *pzErr = sqlite3_mprintf("wrong number of vtable arguments");
   190      rc = SQLITE_ERROR;
   191    }else{
   192      int nByte;                      /* Bytes of space to allocate */
   193      const char *zDb = bDb ? argv[3] : argv[1];
   194      const char *zTab = bDb ? argv[4] : argv[3];
   195      const char *zType = bDb ? argv[5] : argv[4];
   196      int nDb = (int)strlen(zDb)+1; 
   197      int nTab = (int)strlen(zTab)+1;
   198      int eType = 0;
   199      
   200      rc = fts5VocabTableType(zType, pzErr, &eType);
   201      if( rc==SQLITE_OK ){
   202        assert( eType>=0 && eType<ArraySize(azSchema) );
   203        rc = sqlite3_declare_vtab(db, azSchema[eType]);
   204      }
   205  
   206      nByte = sizeof(Fts5VocabTable) + nDb + nTab;
   207      pRet = sqlite3Fts5MallocZero(&rc, nByte);
   208      if( pRet ){
   209        pRet->pGlobal = (Fts5Global*)pAux;
   210        pRet->eType = eType;
   211        pRet->db = db;
   212        pRet->zFts5Tbl = (char*)&pRet[1];
   213        pRet->zFts5Db = &pRet->zFts5Tbl[nTab];
   214        memcpy(pRet->zFts5Tbl, zTab, nTab);
   215        memcpy(pRet->zFts5Db, zDb, nDb);
   216        sqlite3Fts5Dequote(pRet->zFts5Tbl);
   217        sqlite3Fts5Dequote(pRet->zFts5Db);
   218      }
   219    }
   220  
   221    *ppVTab = (sqlite3_vtab*)pRet;
   222    return rc;
   223  }
   224  
   225  
   226  /*
   227  ** The xConnect() and xCreate() methods for the virtual table. All the
   228  ** work is done in function fts5VocabInitVtab().
   229  */
   230  static int fts5VocabConnectMethod(
   231    sqlite3 *db,                    /* Database connection */
   232    void *pAux,                     /* Pointer to tokenizer hash table */
   233    int argc,                       /* Number of elements in argv array */
   234    const char * const *argv,       /* xCreate/xConnect argument array */
   235    sqlite3_vtab **ppVtab,          /* OUT: New sqlite3_vtab object */
   236    char **pzErr                    /* OUT: sqlite3_malloc'd error message */
   237  ){
   238    return fts5VocabInitVtab(db, pAux, argc, argv, ppVtab, pzErr);
   239  }
   240  static int fts5VocabCreateMethod(
   241    sqlite3 *db,                    /* Database connection */
   242    void *pAux,                     /* Pointer to tokenizer hash table */
   243    int argc,                       /* Number of elements in argv array */
   244    const char * const *argv,       /* xCreate/xConnect argument array */
   245    sqlite3_vtab **ppVtab,          /* OUT: New sqlite3_vtab object */
   246    char **pzErr                    /* OUT: sqlite3_malloc'd error message */
   247  ){
   248    return fts5VocabInitVtab(db, pAux, argc, argv, ppVtab, pzErr);
   249  }
   250  
   251  /* 
   252  ** Implementation of the xBestIndex method.
   253  **
   254  ** Only constraints of the form:
   255  **
   256  **     term <= ?
   257  **     term == ?
   258  **     term >= ?
   259  **
   260  ** are interpreted. Less-than and less-than-or-equal are treated 
   261  ** identically, as are greater-than and greater-than-or-equal.
   262  */
   263  static int fts5VocabBestIndexMethod(
   264    sqlite3_vtab *pUnused,
   265    sqlite3_index_info *pInfo
   266  ){
   267    int i;
   268    int iTermEq = -1;
   269    int iTermGe = -1;
   270    int iTermLe = -1;
   271    int idxNum = 0;
   272    int nArg = 0;
   273  
   274    UNUSED_PARAM(pUnused);
   275  
   276    for(i=0; i<pInfo->nConstraint; i++){
   277      struct sqlite3_index_constraint *p = &pInfo->aConstraint[i];
   278      if( p->usable==0 ) continue;
   279      if( p->iColumn==0 ){          /* term column */
   280        if( p->op==SQLITE_INDEX_CONSTRAINT_EQ ) iTermEq = i;
   281        if( p->op==SQLITE_INDEX_CONSTRAINT_LE ) iTermLe = i;
   282        if( p->op==SQLITE_INDEX_CONSTRAINT_LT ) iTermLe = i;
   283        if( p->op==SQLITE_INDEX_CONSTRAINT_GE ) iTermGe = i;
   284        if( p->op==SQLITE_INDEX_CONSTRAINT_GT ) iTermGe = i;
   285      }
   286    }
   287  
   288    if( iTermEq>=0 ){
   289      idxNum |= FTS5_VOCAB_TERM_EQ;
   290      pInfo->aConstraintUsage[iTermEq].argvIndex = ++nArg;
   291      pInfo->estimatedCost = 100;
   292    }else{
   293      pInfo->estimatedCost = 1000000;
   294      if( iTermGe>=0 ){
   295        idxNum |= FTS5_VOCAB_TERM_GE;
   296        pInfo->aConstraintUsage[iTermGe].argvIndex = ++nArg;
   297        pInfo->estimatedCost = pInfo->estimatedCost / 2;
   298      }
   299      if( iTermLe>=0 ){
   300        idxNum |= FTS5_VOCAB_TERM_LE;
   301        pInfo->aConstraintUsage[iTermLe].argvIndex = ++nArg;
   302        pInfo->estimatedCost = pInfo->estimatedCost / 2;
   303      }
   304    }
   305  
   306    /* This virtual table always delivers results in ascending order of
   307    ** the "term" column (column 0). So if the user has requested this
   308    ** specifically - "ORDER BY term" or "ORDER BY term ASC" - set the
   309    ** sqlite3_index_info.orderByConsumed flag to tell the core the results
   310    ** are already in sorted order.  */
   311    if( pInfo->nOrderBy==1 
   312     && pInfo->aOrderBy[0].iColumn==0 
   313     && pInfo->aOrderBy[0].desc==0
   314    ){
   315      pInfo->orderByConsumed = 1;
   316    }
   317  
   318    pInfo->idxNum = idxNum;
   319    return SQLITE_OK;
   320  }
   321  
   322  /*
   323  ** Implementation of xOpen method.
   324  */
   325  static int fts5VocabOpenMethod(
   326    sqlite3_vtab *pVTab, 
   327    sqlite3_vtab_cursor **ppCsr
   328  ){
   329    Fts5VocabTable *pTab = (Fts5VocabTable*)pVTab;
   330    Fts5Index *pIndex = 0;
   331    Fts5Config *pConfig = 0;
   332    Fts5VocabCursor *pCsr = 0;
   333    int rc = SQLITE_OK;
   334    sqlite3_stmt *pStmt = 0;
   335    char *zSql = 0;
   336  
   337    zSql = sqlite3Fts5Mprintf(&rc,
   338        "SELECT t.%Q FROM %Q.%Q AS t WHERE t.%Q MATCH '*id'",
   339        pTab->zFts5Tbl, pTab->zFts5Db, pTab->zFts5Tbl, pTab->zFts5Tbl
   340    );
   341    if( zSql ){
   342      rc = sqlite3_prepare_v2(pTab->db, zSql, -1, &pStmt, 0);
   343    }
   344    sqlite3_free(zSql);
   345    assert( rc==SQLITE_OK || pStmt==0 );
   346    if( rc==SQLITE_ERROR ) rc = SQLITE_OK;
   347  
   348    if( pStmt && sqlite3_step(pStmt)==SQLITE_ROW ){
   349      i64 iId = sqlite3_column_int64(pStmt, 0);
   350      pIndex = sqlite3Fts5IndexFromCsrid(pTab->pGlobal, iId, &pConfig);
   351    }
   352  
   353    if( rc==SQLITE_OK && pIndex==0 ){
   354      rc = sqlite3_finalize(pStmt);
   355      pStmt = 0;
   356      if( rc==SQLITE_OK ){
   357        pVTab->zErrMsg = sqlite3_mprintf(
   358            "no such fts5 table: %s.%s", pTab->zFts5Db, pTab->zFts5Tbl
   359        );
   360        rc = SQLITE_ERROR;
   361      }
   362    }
   363  
   364    if( rc==SQLITE_OK ){
   365      int nByte = pConfig->nCol * sizeof(i64) * 2 + sizeof(Fts5VocabCursor);
   366      pCsr = (Fts5VocabCursor*)sqlite3Fts5MallocZero(&rc, nByte);
   367    }
   368  
   369    if( pCsr ){
   370      pCsr->pIndex = pIndex;
   371      pCsr->pStmt = pStmt;
   372      pCsr->pConfig = pConfig;
   373      pCsr->aCnt = (i64*)&pCsr[1];
   374      pCsr->aDoc = &pCsr->aCnt[pConfig->nCol];
   375    }else{
   376      sqlite3_finalize(pStmt);
   377    }
   378  
   379    *ppCsr = (sqlite3_vtab_cursor*)pCsr;
   380    return rc;
   381  }
   382  
   383  static void fts5VocabResetCursor(Fts5VocabCursor *pCsr){
   384    pCsr->rowid = 0;
   385    sqlite3Fts5IterClose(pCsr->pIter);
   386    pCsr->pIter = 0;
   387    sqlite3_free(pCsr->zLeTerm);
   388    pCsr->nLeTerm = -1;
   389    pCsr->zLeTerm = 0;
   390  }
   391  
   392  /*
   393  ** Close the cursor.  For additional information see the documentation
   394  ** on the xClose method of the virtual table interface.
   395  */
   396  static int fts5VocabCloseMethod(sqlite3_vtab_cursor *pCursor){
   397    Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor;
   398    fts5VocabResetCursor(pCsr);
   399    sqlite3Fts5BufferFree(&pCsr->term);
   400    sqlite3_finalize(pCsr->pStmt);
   401    sqlite3_free(pCsr);
   402    return SQLITE_OK;
   403  }
   404  
   405  static int fts5VocabInstanceNewTerm(Fts5VocabCursor *pCsr){
   406    int rc = SQLITE_OK;
   407    
   408    if( sqlite3Fts5IterEof(pCsr->pIter) ){
   409      pCsr->bEof = 1;
   410    }else{
   411      const char *zTerm;
   412      int nTerm;
   413      zTerm = sqlite3Fts5IterTerm(pCsr->pIter, &nTerm);
   414      if( pCsr->nLeTerm>=0 ){
   415        int nCmp = MIN(nTerm, pCsr->nLeTerm);
   416        int bCmp = memcmp(pCsr->zLeTerm, zTerm, nCmp);
   417        if( bCmp<0 || (bCmp==0 && pCsr->nLeTerm<nTerm) ){
   418          pCsr->bEof = 1;
   419        }
   420      }
   421  
   422      sqlite3Fts5BufferSet(&rc, &pCsr->term, nTerm, (const u8*)zTerm);
   423    }
   424    return rc;
   425  }
   426  
   427  static int fts5VocabInstanceNext(Fts5VocabCursor *pCsr){
   428    int eDetail = pCsr->pConfig->eDetail;
   429    int rc = SQLITE_OK;
   430    Fts5IndexIter *pIter = pCsr->pIter;
   431    i64 *pp = &pCsr->iInstPos;
   432    int *po = &pCsr->iInstOff;
   433    
   434    while( eDetail==FTS5_DETAIL_NONE
   435        || sqlite3Fts5PoslistNext64(pIter->pData, pIter->nData, po, pp) 
   436    ){
   437      pCsr->iInstPos = 0;
   438      pCsr->iInstOff = 0;
   439  
   440      rc = sqlite3Fts5IterNextScan(pCsr->pIter);
   441      if( rc==SQLITE_OK ){
   442        rc = fts5VocabInstanceNewTerm(pCsr);
   443        if( eDetail==FTS5_DETAIL_NONE ) break;
   444      }
   445      if( rc ){
   446        pCsr->bEof = 1;
   447        break;
   448      }
   449    }
   450  
   451    return rc;
   452  }
   453  
   454  /*
   455  ** Advance the cursor to the next row in the table.
   456  */
   457  static int fts5VocabNextMethod(sqlite3_vtab_cursor *pCursor){
   458    Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor;
   459    Fts5VocabTable *pTab = (Fts5VocabTable*)pCursor->pVtab;
   460    int rc = SQLITE_OK;
   461    int nCol = pCsr->pConfig->nCol;
   462  
   463    pCsr->rowid++;
   464  
   465    if( pTab->eType==FTS5_VOCAB_INSTANCE ){
   466      return fts5VocabInstanceNext(pCsr);
   467    }
   468  
   469    if( pTab->eType==FTS5_VOCAB_COL ){
   470      for(pCsr->iCol++; pCsr->iCol<nCol; pCsr->iCol++){
   471        if( pCsr->aDoc[pCsr->iCol] ) break;
   472      }
   473    }
   474  
   475    if( pTab->eType!=FTS5_VOCAB_COL || pCsr->iCol>=nCol ){
   476      if( sqlite3Fts5IterEof(pCsr->pIter) ){
   477        pCsr->bEof = 1;
   478      }else{
   479        const char *zTerm;
   480        int nTerm;
   481  
   482        zTerm = sqlite3Fts5IterTerm(pCsr->pIter, &nTerm);
   483        if( pCsr->nLeTerm>=0 ){
   484          int nCmp = MIN(nTerm, pCsr->nLeTerm);
   485          int bCmp = memcmp(pCsr->zLeTerm, zTerm, nCmp);
   486          if( bCmp<0 || (bCmp==0 && pCsr->nLeTerm<nTerm) ){
   487            pCsr->bEof = 1;
   488            return SQLITE_OK;
   489          }
   490        }
   491  
   492        sqlite3Fts5BufferSet(&rc, &pCsr->term, nTerm, (const u8*)zTerm);
   493        memset(pCsr->aCnt, 0, nCol * sizeof(i64));
   494        memset(pCsr->aDoc, 0, nCol * sizeof(i64));
   495        pCsr->iCol = 0;
   496  
   497        assert( pTab->eType==FTS5_VOCAB_COL || pTab->eType==FTS5_VOCAB_ROW );
   498        while( rc==SQLITE_OK ){
   499          int eDetail = pCsr->pConfig->eDetail;
   500          const u8 *pPos; int nPos;   /* Position list */
   501          i64 iPos = 0;               /* 64-bit position read from poslist */
   502          int iOff = 0;               /* Current offset within position list */
   503  
   504          pPos = pCsr->pIter->pData;
   505          nPos = pCsr->pIter->nData;
   506  
   507          switch( pTab->eType ){
   508            case FTS5_VOCAB_ROW:
   509              if( eDetail==FTS5_DETAIL_FULL ){
   510                while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff, &iPos) ){
   511                  pCsr->aCnt[0]++;
   512                }
   513              }
   514              pCsr->aDoc[0]++;
   515              break;
   516  
   517            case FTS5_VOCAB_COL:
   518              if( eDetail==FTS5_DETAIL_FULL ){
   519                int iCol = -1;
   520                while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff, &iPos) ){
   521                  int ii = FTS5_POS2COLUMN(iPos);
   522                  pCsr->aCnt[ii]++;
   523                  if( iCol!=ii ){
   524                    if( ii>=nCol ){
   525                      rc = FTS5_CORRUPT;
   526                      break;
   527                    }
   528                    pCsr->aDoc[ii]++;
   529                    iCol = ii;
   530                  }
   531                }
   532              }else if( eDetail==FTS5_DETAIL_COLUMNS ){
   533                while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff,&iPos) ){
   534                  assert_nc( iPos>=0 && iPos<nCol );
   535                  if( iPos>=nCol ){
   536                    rc = FTS5_CORRUPT;
   537                    break;
   538                  }
   539                  pCsr->aDoc[iPos]++;
   540                }
   541              }else{
   542                assert( eDetail==FTS5_DETAIL_NONE );
   543                pCsr->aDoc[0]++;
   544              }
   545              break;
   546  
   547            default:
   548              assert( pTab->eType==FTS5_VOCAB_INSTANCE );
   549              break;
   550          }
   551  
   552          if( rc==SQLITE_OK ){
   553            rc = sqlite3Fts5IterNextScan(pCsr->pIter);
   554          }
   555          if( pTab->eType==FTS5_VOCAB_INSTANCE ) break;
   556  
   557          if( rc==SQLITE_OK ){
   558            zTerm = sqlite3Fts5IterTerm(pCsr->pIter, &nTerm);
   559            if( nTerm!=pCsr->term.n || memcmp(zTerm, pCsr->term.p, nTerm) ){
   560              break;
   561            }
   562            if( sqlite3Fts5IterEof(pCsr->pIter) ) break;
   563          }
   564        }
   565      }
   566    }
   567  
   568    if( rc==SQLITE_OK && pCsr->bEof==0 && pTab->eType==FTS5_VOCAB_COL ){
   569      while( pCsr->aDoc[pCsr->iCol]==0 ) pCsr->iCol++;
   570      assert( pCsr->iCol<pCsr->pConfig->nCol );
   571    }
   572    return rc;
   573  }
   574  
   575  /*
   576  ** This is the xFilter implementation for the virtual table.
   577  */
   578  static int fts5VocabFilterMethod(
   579    sqlite3_vtab_cursor *pCursor,   /* The cursor used for this query */
   580    int idxNum,                     /* Strategy index */
   581    const char *zUnused,            /* Unused */
   582    int nUnused,                    /* Number of elements in apVal */
   583    sqlite3_value **apVal           /* Arguments for the indexing scheme */
   584  ){
   585    Fts5VocabTable *pTab = (Fts5VocabTable*)pCursor->pVtab;
   586    Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor;
   587    int eType = pTab->eType;
   588    int rc = SQLITE_OK;
   589  
   590    int iVal = 0;
   591    int f = FTS5INDEX_QUERY_SCAN;
   592    const char *zTerm = 0;
   593    int nTerm = 0;
   594  
   595    sqlite3_value *pEq = 0;
   596    sqlite3_value *pGe = 0;
   597    sqlite3_value *pLe = 0;
   598  
   599    UNUSED_PARAM2(zUnused, nUnused);
   600  
   601    fts5VocabResetCursor(pCsr);
   602    if( idxNum & FTS5_VOCAB_TERM_EQ ) pEq = apVal[iVal++];
   603    if( idxNum & FTS5_VOCAB_TERM_GE ) pGe = apVal[iVal++];
   604    if( idxNum & FTS5_VOCAB_TERM_LE ) pLe = apVal[iVal++];
   605  
   606    if( pEq ){
   607      zTerm = (const char *)sqlite3_value_text(pEq);
   608      nTerm = sqlite3_value_bytes(pEq);
   609      f = 0;
   610    }else{
   611      if( pGe ){
   612        zTerm = (const char *)sqlite3_value_text(pGe);
   613        nTerm = sqlite3_value_bytes(pGe);
   614      }
   615      if( pLe ){
   616        const char *zCopy = (const char *)sqlite3_value_text(pLe);
   617        pCsr->nLeTerm = sqlite3_value_bytes(pLe);
   618        pCsr->zLeTerm = sqlite3_malloc(pCsr->nLeTerm+1);
   619        if( pCsr->zLeTerm==0 ){
   620          rc = SQLITE_NOMEM;
   621        }else{
   622          memcpy(pCsr->zLeTerm, zCopy, pCsr->nLeTerm+1);
   623        }
   624      }
   625    }
   626  
   627    if( rc==SQLITE_OK ){
   628      rc = sqlite3Fts5IndexQuery(pCsr->pIndex, zTerm, nTerm, f, 0, &pCsr->pIter);
   629    }
   630    if( rc==SQLITE_OK && eType==FTS5_VOCAB_INSTANCE ){
   631      rc = fts5VocabInstanceNewTerm(pCsr);
   632    }
   633    if( rc==SQLITE_OK 
   634     && !pCsr->bEof 
   635     && (eType!=FTS5_VOCAB_INSTANCE || pCsr->pConfig->eDetail!=FTS5_DETAIL_NONE)
   636    ){
   637      rc = fts5VocabNextMethod(pCursor);
   638    }
   639  
   640    return rc;
   641  }
   642  
   643  /* 
   644  ** This is the xEof method of the virtual table. SQLite calls this 
   645  ** routine to find out if it has reached the end of a result set.
   646  */
   647  static int fts5VocabEofMethod(sqlite3_vtab_cursor *pCursor){
   648    Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor;
   649    return pCsr->bEof;
   650  }
   651  
   652  static int fts5VocabColumnMethod(
   653    sqlite3_vtab_cursor *pCursor,   /* Cursor to retrieve value from */
   654    sqlite3_context *pCtx,          /* Context for sqlite3_result_xxx() calls */
   655    int iCol                        /* Index of column to read value from */
   656  ){
   657    Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor;
   658    int eDetail = pCsr->pConfig->eDetail;
   659    int eType = ((Fts5VocabTable*)(pCursor->pVtab))->eType;
   660    i64 iVal = 0;
   661  
   662    if( iCol==0 ){
   663      sqlite3_result_text(
   664          pCtx, (const char*)pCsr->term.p, pCsr->term.n, SQLITE_TRANSIENT
   665      );
   666    }else if( eType==FTS5_VOCAB_COL ){
   667      assert( iCol==1 || iCol==2 || iCol==3 );
   668      if( iCol==1 ){
   669        if( eDetail!=FTS5_DETAIL_NONE ){
   670          const char *z = pCsr->pConfig->azCol[pCsr->iCol];
   671          sqlite3_result_text(pCtx, z, -1, SQLITE_STATIC);
   672        }
   673      }else if( iCol==2 ){
   674        iVal = pCsr->aDoc[pCsr->iCol];
   675      }else{
   676        iVal = pCsr->aCnt[pCsr->iCol];
   677      }
   678    }else if( eType==FTS5_VOCAB_ROW ){
   679      assert( iCol==1 || iCol==2 );
   680      if( iCol==1 ){
   681        iVal = pCsr->aDoc[0];
   682      }else{
   683        iVal = pCsr->aCnt[0];
   684      }
   685    }else{
   686      assert( eType==FTS5_VOCAB_INSTANCE );
   687      switch( iCol ){
   688        case 1:
   689          sqlite3_result_int64(pCtx, pCsr->pIter->iRowid);
   690          break;
   691        case 2: {
   692          int ii = -1;
   693          if( eDetail==FTS5_DETAIL_FULL ){
   694            ii = FTS5_POS2COLUMN(pCsr->iInstPos);
   695          }else if( eDetail==FTS5_DETAIL_COLUMNS ){
   696            ii = (int)pCsr->iInstPos;
   697          }
   698          if( ii>=0 && ii<pCsr->pConfig->nCol ){
   699            const char *z = pCsr->pConfig->azCol[ii];
   700            sqlite3_result_text(pCtx, z, -1, SQLITE_STATIC);
   701          }
   702          break;
   703        }
   704        default: {
   705          assert( iCol==3 );
   706          if( eDetail==FTS5_DETAIL_FULL ){
   707            int ii = FTS5_POS2OFFSET(pCsr->iInstPos);
   708            sqlite3_result_int(pCtx, ii);
   709          }
   710          break;
   711        }
   712      }
   713    }
   714  
   715    if( iVal>0 ) sqlite3_result_int64(pCtx, iVal);
   716    return SQLITE_OK;
   717  }
   718  
   719  /* 
   720  ** This is the xRowid method. The SQLite core calls this routine to
   721  ** retrieve the rowid for the current row of the result set. The
   722  ** rowid should be written to *pRowid.
   723  */
   724  static int fts5VocabRowidMethod(
   725    sqlite3_vtab_cursor *pCursor, 
   726    sqlite_int64 *pRowid
   727  ){
   728    Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor;
   729    *pRowid = pCsr->rowid;
   730    return SQLITE_OK;
   731  }
   732  
   733  int sqlite3Fts5VocabInit(Fts5Global *pGlobal, sqlite3 *db){
   734    static const sqlite3_module fts5Vocab = {
   735      /* iVersion      */ 2,
   736      /* xCreate       */ fts5VocabCreateMethod,
   737      /* xConnect      */ fts5VocabConnectMethod,
   738      /* xBestIndex    */ fts5VocabBestIndexMethod,
   739      /* xDisconnect   */ fts5VocabDisconnectMethod,
   740      /* xDestroy      */ fts5VocabDestroyMethod,
   741      /* xOpen         */ fts5VocabOpenMethod,
   742      /* xClose        */ fts5VocabCloseMethod,
   743      /* xFilter       */ fts5VocabFilterMethod,
   744      /* xNext         */ fts5VocabNextMethod,
   745      /* xEof          */ fts5VocabEofMethod,
   746      /* xColumn       */ fts5VocabColumnMethod,
   747      /* xRowid        */ fts5VocabRowidMethod,
   748      /* xUpdate       */ 0,
   749      /* xBegin        */ 0,
   750      /* xSync         */ 0,
   751      /* xCommit       */ 0,
   752      /* xRollback     */ 0,
   753      /* xFindFunction */ 0,
   754      /* xRename       */ 0,
   755      /* xSavepoint    */ 0,
   756      /* xRelease      */ 0,
   757      /* xRollbackTo   */ 0,
   758    };
   759    void *p = (void*)pGlobal;
   760  
   761    return sqlite3_create_module_v2(db, "fts5vocab", &fts5Vocab, p, 0);
   762  }
   763  
   764