/ Check-in [39222761]
Login

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

Overview
Comment:Allow the fts5vocab table to optionally provide data on a per-column basis.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | fts5
Files: files | file ages | folders
SHA1: 3922276135a7825d0ede8d9c757e9cfe492f803a
User & Date: dan 2015-05-09 18:28:27
Context
2015-05-13
17:15
Change fts5 doclist-index structures to be trees instead of flat lists. This only makes a difference for databases that contain millions of instances of the same token. check-in: aa34bf66 user: dan tags: fts5
2015-05-09
18:28
Allow the fts5vocab table to optionally provide data on a per-column basis. check-in: 39222761 user: dan tags: fts5
2015-05-08
20:21
Add the fts5vocab module, for direct access to the fts5 index. check-in: 6bf93e3b user: dan tags: fts5
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to ext/fts5/fts5.c.

  1666   1666     }
  1667   1667   }
  1668   1668   
  1669   1669   
  1670   1670   /*
  1671   1671   ** Given cursor id iId, return a pointer to the corresponding Fts5Index 
  1672   1672   ** object. Or NULL If the cursor id does not exist.
         1673  +**
         1674  +** If successful, set *pnCol to the number of indexed columns in the
         1675  +** table before returning.
  1673   1676   */
  1674         -Fts5Index *sqlite3Fts5IndexFromCsrid(Fts5Global *pGlobal, i64 iCsrId){
         1677  +Fts5Index *sqlite3Fts5IndexFromCsrid(
         1678  +  Fts5Global *pGlobal, 
         1679  +  i64 iCsrId, 
         1680  +  int *pnCol
         1681  +){
  1675   1682     Fts5Cursor *pCsr;
  1676   1683     Fts5Index *pIndex = 0;
  1677   1684   
  1678   1685     for(pCsr=pGlobal->pCsr; pCsr; pCsr=pCsr->pNext){
  1679   1686       if( pCsr->iCsrId==iCsrId ) break;
  1680   1687     }
  1681   1688     if( pCsr ){
  1682   1689       Fts5Table *pTab = (Fts5Table*)pCsr->base.pVtab;
  1683   1690       pIndex = pTab->pIndex;
         1691  +    *pnCol = pTab->pConfig->nCol;
  1684   1692     }
  1685   1693   
  1686   1694     return pIndex;
  1687   1695   }
  1688   1696   
  1689   1697   /*
  1690   1698   ** Return a "position-list blob" corresponding to the current position of

Changes to ext/fts5/fts5Int.h.

   391    391     const char **azArg,
   392    392     int nArg,
   393    393     Fts5Tokenizer**,
   394    394     fts5_tokenizer**,
   395    395     char **pzErr
   396    396   );
   397    397   
   398         -Fts5Index *sqlite3Fts5IndexFromCsrid(Fts5Global*, i64);
          398  +Fts5Index *sqlite3Fts5IndexFromCsrid(Fts5Global*, i64, int*);
   399    399   
   400    400   /*
   401    401   ** End of interface to code in fts5.c.
   402    402   **************************************************************************/
   403    403   
   404    404   /**************************************************************************
   405    405   ** Interface to code in fts5_hash.c. 

Changes to ext/fts5/fts5_vocab.c.

     6      6   **
     7      7   **    May you do good and not evil.
     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         -** This is an SQLite module implementing full-text search.
           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.
    14     32   */
    15     33   
    16     34   #if defined(SQLITE_ENABLE_FTS5)
    17     35   
    18     36   #include "fts5Int.h"
    19     37   
    20     38   
................................................................................
    23     41   
    24     42   struct Fts5VocabTable {
    25     43     sqlite3_vtab base;
    26     44     char *zFts5Tbl;                 /* Name of fts5 table */
    27     45     char *zFts5Db;                  /* Db containing fts5 table */
    28     46     sqlite3 *db;                    /* Database handle */
    29     47     Fts5Global *pGlobal;            /* FTS5 global object for this database */
           48  +  int eType;                      /* FTS5_VOCAB_COL or ROW */
    30     49   };
    31     50   
    32     51   struct Fts5VocabCursor {
    33     52     sqlite3_vtab_cursor base;
    34     53     sqlite3_stmt *pStmt;            /* Statement holding lock on pIndex */
    35     54     Fts5Index *pIndex;              /* Associated FTS5 index */
    36     55   
    37         -  Fts5IndexIter *pIter;           /* Iterator object */
    38     56     int bEof;                       /* True if this cursor is at EOF */
           57  +  Fts5IndexIter *pIter;           /* Term/rowid iterator object */
           58  +
           59  +  /* These are used by 'col' tables only */
           60  +  int nCol;
           61  +  int iCol;
           62  +  i64 *aCnt;
           63  +  i64 *aDoc;
           64  +
           65  +  /* Output values */
           66  +  i64 rowid;                      /* This table's current rowid value */
    39     67     Fts5Buffer term;                /* Current value of 'term' column */
    40         -  i64 nRow;                       /* Current value of 'row' column */
    41         -  i64 nInst;                      /* Current value of 'inst' column */
    42         -  i64 rowid;                      /* Current value of rowid column */
           68  +  i64 aVal[3];                    /* Up to three columns left of 'term' */
    43     69   };
           70  +
           71  +#define FTS5_VOCAB_COL    0
           72  +#define FTS5_VOCAB_ROW    1
           73  +
           74  +#define FTS5_VOCAB_COL_SCHEMA  "term, col, doc, cnt"
           75  +#define FTS5_VOCAB_ROW_SCHEMA  "term, doc, cnt"
           76  +
           77  +/*
           78  +** Translate a string containing an fts5vocab table type to an 
           79  +** FTS5_VOCAB_XXX constant. If successful, set *peType to the output
           80  +** value and return SQLITE_OK. Otherwise, set *pzErr to an error message
           81  +** and return SQLITE_ERROR.
           82  +*/
           83  +static int fts5VocabTableType(const char *zType, char **pzErr, int *peType){
           84  +  int rc = SQLITE_OK;
           85  +  char *zCopy = sqlite3Fts5Strndup(&rc, zType, -1);
           86  +  if( rc==SQLITE_OK ){
           87  +    sqlite3Fts5Dequote(zCopy);
           88  +    if( sqlite3_stricmp(zCopy, "col")==0 ){
           89  +      *peType = FTS5_VOCAB_COL;
           90  +    }else
           91  +
           92  +    if( sqlite3_stricmp(zCopy, "row")==0 ){
           93  +      *peType = FTS5_VOCAB_ROW;
           94  +    }else
           95  +    {
           96  +      *pzErr = sqlite3_mprintf("fts5vocab: unknown table type: %Q", zCopy);
           97  +      rc = SQLITE_ERROR;
           98  +    }
           99  +    sqlite3_free(zCopy);
          100  +  }
          101  +
          102  +  return rc;
          103  +}
    44    104   
    45    105   
    46    106   /*
    47    107   ** The xDisconnect() virtual table method.
    48    108   */
    49    109   static int fts5VocabDisconnectMethod(sqlite3_vtab *pVtab){
    50    110     Fts5VocabTable *pTab = (Fts5VocabTable*)pVtab;
................................................................................
    66    126   ** methods of the FTS3 virtual table.
    67    127   **
    68    128   ** The argv[] array contains the following:
    69    129   **
    70    130   **   argv[0]   -> module name  ("fts5vocab")
    71    131   **   argv[1]   -> database name
    72    132   **   argv[2]   -> table name
          133  +**
          134  +** then:
          135  +**
    73    136   **   argv[3]   -> name of fts5 table
          137  +**   argv[4]   -> type of fts5vocab table
          138  +**
          139  +** or, for tables in the TEMP schema only.
          140  +**
          141  +**   argv[3]   -> name of fts5 tables database
          142  +**   argv[4]   -> name of fts5 table
          143  +**   argv[5]   -> type of fts5vocab table
    74    144   */
    75    145   static int fts5VocabInitVtab(
    76    146     sqlite3 *db,                    /* The SQLite database connection */
    77    147     void *pAux,                     /* Pointer to Fts5Global object */
    78    148     int argc,                       /* Number of elements in argv array */
    79    149     const char * const *argv,       /* xCreate/xConnect argument array */
    80    150     sqlite3_vtab **ppVTab,          /* Write the resulting vtab structure here */
    81    151     char **pzErr                    /* Write any error message here */
    82    152   ){
    83         -  const char *zSchema = "CREATE TABLE vvv(term, row, inst)";
          153  +  const char *azSchema[] = { 
          154  +    "CREATE TABlE vocab(" FTS5_VOCAB_COL_SCHEMA  ")", 
          155  +    "CREATE TABlE vocab(" FTS5_VOCAB_ROW_SCHEMA  ")"
          156  +  };
          157  +
    84    158     Fts5VocabTable *pRet = 0;
    85    159     int rc = SQLITE_OK;             /* Return code */
          160  +  int bDb;
    86    161   
    87         -  if( argc!=4 ){
          162  +  bDb = (argc==6 && strlen(argv[1])==4 && memcmp("temp", argv[1], 4)==0);
          163  +
          164  +  if( argc!=5 && bDb==0 ){
    88    165       *pzErr = sqlite3_mprintf("wrong number of vtable arguments");
    89    166       rc = SQLITE_ERROR;
    90    167     }else{
    91    168       int nByte;                      /* Bytes of space to allocate */
    92         -    const char *zDb = argv[1];
    93         -    const char *zTab = argv[3];
    94         -    int nDb = strlen(zDb) + 1;
    95         -    int nTab = strlen(zTab) + 1;
    96         -
    97         -    rc = sqlite3_declare_vtab(db, zSchema);
          169  +    const char *zDb = bDb ? argv[3] : argv[1];
          170  +    const char *zTab = bDb ? argv[4] : argv[3];
          171  +    const char *zType = bDb ? argv[5] : argv[4];
          172  +    int nDb = strlen(zDb)+1; 
          173  +    int nTab = strlen(zTab)+1;
          174  +    int eType;
          175  +    
          176  +    rc = fts5VocabTableType(zType, pzErr, &eType);
          177  +    if( rc==SQLITE_OK ){
          178  +      assert( eType>=0 && eType<sizeof(azSchema)/sizeof(azSchema[0]) );
          179  +      rc = sqlite3_declare_vtab(db, azSchema[eType]);
          180  +    }
    98    181   
    99    182       nByte = sizeof(Fts5VocabTable) + nDb + nTab;
   100    183       pRet = sqlite3Fts5MallocZero(&rc, nByte);
   101    184       if( pRet ){
   102    185         pRet->pGlobal = (Fts5Global*)pAux;
          186  +      pRet->eType = eType;
   103    187         pRet->db = db;
   104    188         pRet->zFts5Tbl = (char*)&pRet[1];
   105    189         pRet->zFts5Db = &pRet->zFts5Tbl[nTab];
   106    190         memcpy(pRet->zFts5Tbl, zTab, nTab);
   107    191         memcpy(pRet->zFts5Db, zDb, nDb);
   108    192       }
   109    193     }
................................................................................
   152    236   ** Implementation of xOpen method.
   153    237   */
   154    238   static int fts5VocabOpenMethod(
   155    239     sqlite3_vtab *pVTab, 
   156    240     sqlite3_vtab_cursor **ppCsr
   157    241   ){
   158    242     Fts5VocabTable *pTab = (Fts5VocabTable*)pVTab;
   159         -  Fts5VocabCursor *pCsr;
          243  +  Fts5Index *pIndex = 0;
          244  +  int nCol = 0;
          245  +  Fts5VocabCursor *pCsr = 0;
   160    246     int rc = SQLITE_OK;
   161         -
   162         -  pCsr = (Fts5VocabCursor*)sqlite3Fts5MallocZero(&rc, sizeof(Fts5VocabCursor));
   163         -  if( pCsr ){
   164         -    char *zSql = sqlite3_mprintf(
   165         -        "SELECT t.%Q FROM %Q.%Q AS t WHERE t.%Q MATCH '*id'",
   166         -        pTab->zFts5Tbl, pTab->zFts5Db, pTab->zFts5Tbl, pTab->zFts5Tbl
   167         -    );
   168         -    if( zSql==0 ){
   169         -      rc = SQLITE_NOMEM;
   170         -    }else{
   171         -      rc = sqlite3_prepare_v2(pTab->db, zSql, -1, &pCsr->pStmt, 0);
   172         -    }
   173         -    sqlite3_free(zSql);
   174         -    if( rc==SQLITE_OK && sqlite3_step(pCsr->pStmt)==SQLITE_ROW ){
   175         -      i64 iId = sqlite3_column_int64(pCsr->pStmt, 0);
   176         -      pCsr->pIndex = sqlite3Fts5IndexFromCsrid(pTab->pGlobal, iId);
   177         -    }
   178         -
   179         -    if( rc==SQLITE_OK && pCsr->pIndex==0 ){
   180         -      rc = sqlite3_finalize(pCsr->pStmt);
   181         -      pCsr->pStmt = 0;
   182         -      if( rc==SQLITE_OK ){
   183         -        pVTab->zErrMsg = sqlite3_mprintf(
   184         -            "no such fts5 table: %Q.%Q", pTab->zFts5Db, pTab->zFts5Tbl
   185         -        );
   186         -        rc = SQLITE_ERROR;
   187         -      }
   188         -    }
   189         -
   190         -    if( rc!=SQLITE_OK ){
   191         -      sqlite3_free(pCsr);
   192         -      pCsr = 0;
          247  +  sqlite3_stmt *pStmt = 0;
          248  +  char *zSql = 0;
          249  +  int nByte;
          250  +
          251  +  zSql = sqlite3_mprintf(
          252  +      "SELECT t.%Q FROM %Q.%Q AS t WHERE t.%Q MATCH '*id'",
          253  +      pTab->zFts5Tbl, pTab->zFts5Db, pTab->zFts5Tbl, pTab->zFts5Tbl
          254  +  );
          255  +  if( zSql==0 ){
          256  +    rc = SQLITE_NOMEM;
          257  +  }else{
          258  +    rc = sqlite3_prepare_v2(pTab->db, zSql, -1, &pStmt, 0);
          259  +  }
          260  +  sqlite3_free(zSql);
          261  +
          262  +  if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){
          263  +    i64 iId = sqlite3_column_int64(pStmt, 0);
          264  +    pIndex = sqlite3Fts5IndexFromCsrid(pTab->pGlobal, iId, &nCol);
          265  +  }
          266  +
          267  +  if( rc==SQLITE_OK && pIndex==0 ){
          268  +    rc = sqlite3_finalize(pStmt);
          269  +    pStmt = 0;
          270  +    if( rc==SQLITE_OK ){
          271  +      pVTab->zErrMsg = sqlite3_mprintf(
          272  +          "no such fts5 table: %Q.%Q", pTab->zFts5Db, pTab->zFts5Tbl
          273  +          );
          274  +      rc = SQLITE_ERROR;
   193    275       }
   194    276     }
   195    277   
          278  +  nByte = nCol * sizeof(i64) * 2 + sizeof(Fts5VocabCursor);
          279  +  pCsr = (Fts5VocabCursor*)sqlite3Fts5MallocZero(&rc, nByte);
          280  +  if( pCsr ){
          281  +    pCsr->pIndex = pIndex;
          282  +    pCsr->pStmt = pStmt;
          283  +    pCsr->nCol = nCol;
          284  +    pCsr->aCnt = (i64*)&pCsr[1];
          285  +    pCsr->aDoc = &pCsr->aCnt[nCol];
          286  +  }else{
          287  +    sqlite3_finalize(pStmt);
          288  +  }
   196    289   
   197    290     *ppCsr = (sqlite3_vtab_cursor*)pCsr;
   198    291     return rc;
   199    292   }
   200    293   
   201    294   static void fts5VocabResetCursor(Fts5VocabCursor *pCsr){
   202    295     pCsr->rowid = 0;
................................................................................
   221    314   
   222    315   
   223    316   /*
   224    317   ** Advance the cursor to the next row in the table.
   225    318   */
   226    319   static int fts5VocabNextMethod(sqlite3_vtab_cursor *pCursor){
   227    320     Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor;
          321  +  Fts5VocabTable *pTab = (Fts5VocabTable*)pCursor->pVtab;
   228    322     int rc = SQLITE_OK;
   229    323   
   230         -  if( sqlite3Fts5IterEof(pCsr->pIter) ){
   231         -    pCsr->bEof = 1;
   232         -  }else{
   233         -    const char *zTerm;
   234         -    int nTerm;
          324  +  pCsr->rowid++;
   235    325   
   236         -    zTerm = sqlite3Fts5IterTerm(pCsr->pIter, &nTerm);
   237         -    sqlite3Fts5BufferSet(&rc, &pCsr->term, nTerm, (const u8*)zTerm);
   238         -    pCsr->nInst = 0;
   239         -    pCsr->nRow = 0;
   240         -    pCsr->rowid++;
          326  +  if( pTab->eType==FTS5_VOCAB_COL ){
          327  +    for(pCsr->iCol++; pCsr->iCol<pCsr->nCol; pCsr->iCol++){
          328  +      if( pCsr->aCnt[pCsr->iCol] ) break;
          329  +    }
          330  +  }
   241    331   
   242         -    while( 1 ){
   243         -      const u8 *pPos; int nPos;   /* Position list */
   244         -      i64 dummy = 0;
   245         -      int iOff = 0;
          332  +  if( pTab->eType==FTS5_VOCAB_ROW || pCsr->iCol>=pCsr->nCol ){
          333  +    if( sqlite3Fts5IterEof(pCsr->pIter) ){
          334  +      pCsr->bEof = 1;
          335  +    }else{
          336  +      const char *zTerm;
          337  +      int nTerm;
   246    338   
   247         -      rc = sqlite3Fts5IterPoslist(pCsr->pIter, &pPos, &nPos);
   248         -      if( rc!=SQLITE_OK ) break;
   249         -      while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff, &dummy) ){
   250         -        pCsr->nInst++;
   251         -      }
   252         -      pCsr->nRow++;
   253         -
   254         -      rc = sqlite3Fts5IterNextScan(pCsr->pIter);
   255         -      if( rc!=SQLITE_OK ) break;
   256    339         zTerm = sqlite3Fts5IterTerm(pCsr->pIter, &nTerm);
   257         -      if( nTerm!=pCsr->term.n || memcmp(zTerm, pCsr->term.p, nTerm) ) break;
   258         -      if( sqlite3Fts5IterEof(pCsr->pIter) ) break;
          340  +      sqlite3Fts5BufferSet(&rc, &pCsr->term, nTerm, (const u8*)zTerm);
          341  +      memset(pCsr->aVal, 0, sizeof(pCsr->aVal));
          342  +      memset(pCsr->aCnt, 0, pCsr->nCol * sizeof(i64));
          343  +      memset(pCsr->aDoc, 0, pCsr->nCol * sizeof(i64));
          344  +      pCsr->iCol = 0;
          345  +
          346  +      assert( pTab->eType==FTS5_VOCAB_COL || pTab->eType==FTS5_VOCAB_ROW );
          347  +      while( 1 ){
          348  +        const u8 *pPos; int nPos;   /* Position list */
          349  +        i64 iPos = 0;               /* 64-bit position read from poslist */
          350  +        int iOff = 0;               /* Current offset within position list */
          351  +
          352  +        rc = sqlite3Fts5IterPoslist(pCsr->pIter, &pPos, &nPos);
          353  +        if( rc!=SQLITE_OK ) break;
          354  +
          355  +        if( pTab->eType==FTS5_VOCAB_ROW ){
          356  +          while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff, &iPos) ){
          357  +            pCsr->aVal[1]++;
          358  +          }
          359  +          pCsr->aVal[0]++;
          360  +        }else{
          361  +          int iCol = -1;
          362  +          while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff, &iPos) ){
          363  +            int ii = FTS5_POS2COLUMN(iPos);
          364  +            pCsr->aCnt[ii]++;
          365  +            if( iCol!=ii ){
          366  +              pCsr->aDoc[ii]++;
          367  +              iCol = ii;
          368  +            }
          369  +          }
          370  +        }
          371  +
          372  +        rc = sqlite3Fts5IterNextScan(pCsr->pIter);
          373  +        if( rc!=SQLITE_OK ) break;
          374  +        zTerm = sqlite3Fts5IterTerm(pCsr->pIter, &nTerm);
          375  +        if( nTerm!=pCsr->term.n || memcmp(zTerm, pCsr->term.p, nTerm) ) break;
          376  +        if( sqlite3Fts5IterEof(pCsr->pIter) ) break;
          377  +      }
   259    378       }
   260    379     }
          380  +
          381  +  if( pCsr->bEof==0 && pTab->eType==FTS5_VOCAB_COL ){
          382  +    while( pCsr->aCnt[pCsr->iCol]==0 ) pCsr->iCol++;
          383  +    pCsr->aVal[0] = pCsr->iCol;
          384  +    pCsr->aVal[1] = pCsr->aDoc[pCsr->iCol];
          385  +    pCsr->aVal[2] = pCsr->aCnt[pCsr->iCol];
          386  +  }
   261    387     return rc;
   262    388   }
   263    389   
   264    390   /*
   265    391   ** This is the xFilter implementation for the virtual table.
   266    392   */
   267    393   static int fts5VocabFilterMethod(
................................................................................
   302    428     switch( iCol ){
   303    429       case 0: /* term */
   304    430         sqlite3_result_text(
   305    431             pCtx, (const char*)pCsr->term.p, pCsr->term.n, SQLITE_TRANSIENT
   306    432         );
   307    433         break;
   308    434   
   309         -    case 1: /* row */
   310         -      sqlite3_result_int64(pCtx, pCsr->nRow);
   311         -      break;
   312         -
   313         -    case 2: /* inst */
   314         -      sqlite3_result_int64(pCtx, pCsr->nInst);
   315         -      break;
   316         -
   317    435       default:
   318         -      assert( 0 );
          436  +      assert( iCol<4 && iCol>0 );
          437  +      sqlite3_result_int64(pCtx, pCsr->aVal[iCol-1]);
          438  +      break;
   319    439     }
   320    440     return SQLITE_OK;
   321    441   }
   322    442   
   323    443   /* 
   324    444   ** This is the xRowid method. The SQLite core calls this routine to
   325    445   ** retrieve the rowid for the current row of the result set. fts5

Changes to ext/fts5/test/fts5vocab.test.

    12     12   # The tests in this file focus on testing the fts5vocab module.
    13     13   #
    14     14   
    15     15   source [file join [file dirname [info script]] fts5_common.tcl]
    16     16   set testprefix fts5vocab
    17     17   
    18     18   
    19         -do_execsql_test 1.1 {
           19  +do_execsql_test 1.1.1 {
    20     20     CREATE VIRTUAL TABLE t1 USING fts5(one, prefix=1);
    21         -  CREATE VIRTUAL TABLE v1 USING fts5vocab(t1);
           21  +  CREATE VIRTUAL TABLE v1 USING fts5vocab(t1, 'row');
    22     22     PRAGMA table_info = v1;
    23     23   } {
    24     24     0 term {} 0 {} 0
    25         -  1 row {} 0 {} 0
    26         -  2 inst {} 0 {} 0
           25  +  1 doc {} 0 {} 0
           26  +  2 cnt {} 0 {} 0
           27  +}
           28  +
           29  +do_execsql_test 1.1.2 {
           30  +  CREATE VIRTUAL TABLE v2 USING fts5vocab(t1, 'col');
           31  +  PRAGMA table_info = v2;
           32  +} {
           33  +  0 term {} 0 {} 0
           34  +  1 col {} 0 {} 0
           35  +  2 doc {} 0 {} 0
           36  +  3 cnt {} 0 {} 0
    27     37   }
    28     38   
    29         -do_execsql_test 1.2 { SELECT * FROM v1 } { }
           39  +do_execsql_test 1.2.1 { SELECT * FROM v1 } { }
           40  +do_execsql_test 1.2.2 { SELECT * FROM v2 } { }
    30     41   
    31     42   do_execsql_test 1.3 {
    32     43     INSERT INTO t1 VALUES('x y z');
    33     44     INSERT INTO t1 VALUES('x x x');
    34     45   }
    35     46   
    36         -do_execsql_test 1.4 {
           47  +do_execsql_test 1.4.1 {
    37     48     SELECT * FROM v1;
    38     49   } {x 2 4  y 1 1  z 1 1}
    39     50   
    40         -do_execsql_test 1.5 {
           51  +do_execsql_test 1.4.2 {
           52  +  SELECT * FROM v2;
           53  +} {x 0 2 4  y 0 1 1  z 0 1 1}
           54  +
           55  +do_execsql_test 1.5.1 {
    41     56     BEGIN;
    42     57       INSERT INTO t1 VALUES('a b c');
    43     58       SELECT * FROM v1 WHERE term<'d';
    44         -  COMMIT;
    45     59   } {a 1 1   b 1 1   c 1 1}
    46     60   
           61  +do_execsql_test 1.5.2 {
           62  +    SELECT * FROM v2 WHERE term<'d';
           63  +  COMMIT;
           64  +} {a 0 1 1  b 0 1 1  c 0 1 1}
           65  +
    47     66   do_execsql_test 1.6 {
    48     67     DELETE FROM t1 WHERE one = 'a b c';
    49     68     SELECT * FROM v1;
    50     69   } {x 2 4  y 1 1  z 1 1}
    51     70   
           71  +#-------------------------------------------------------------------------
           72  +#
           73  +do_execsql_test 2.0 {
           74  +  CREATE VIRTUAL TABLE tt USING fts5(a, b);
           75  +  INSERT INTO tt VALUES('d g b f d f', 'f c e c d a');
           76  +  INSERT INTO tt VALUES('f a e a a b', 'e d c f d d');
           77  +  INSERT INTO tt VALUES('b c a a a b', 'f f c c b c');
           78  +  INSERT INTO tt VALUES('f d c a c e', 'd g d e g d');
           79  +  INSERT INTO tt VALUES('g d e f a g x', 'f f d a a b');
           80  +  INSERT INTO tt VALUES('g c f b c g', 'a g f d c b');
           81  +  INSERT INTO tt VALUES('c e c f g b', 'f e d b g a');
           82  +  INSERT INTO tt VALUES('g d e f d e', 'a c d b a g');
           83  +  INSERT INTO tt VALUES('e f a c c b', 'b f e a f d y');
           84  +  INSERT INTO tt VALUES('c c a a c f', 'd g a e b g');
           85  +  CREATE VIRTUAL TABLE tv USING fts5vocab(tt, 'col');
           86  +  SELECT * FROM tv;
           87  +} {
           88  +  a 0 6 11    a 1 7 9
           89  +  b 0 6 7     b 1 7 7 
           90  +  c 0 6 12    c 1 5 8 
           91  +  d 0 4 6     d 1 9 13 
           92  +  e 0 6 7     e 1 6 6 
           93  +  f 0 9 10    f 1 7 10 
           94  +  g 0 5 7     g 1 5 7
           95  +  x 0 1 1     y 1 1 1
           96  +}
    52     97   
           98  +do_execsql_test 2.1 {
           99  +  CREATE VIRTUAL TABLE temp.tv2 USING fts5vocab(main, tt, 'row');
          100  +  SELECT * FROM tv2;
          101  +} {
          102  +  a 10 20   b 9 14   c 9 20   d 9 19   
          103  +  e 8 13   f 10 20   g 7 14   x 1 1   
          104  +  y 1 1
          105  +}
    53    106   
          107  +#-------------------------------------------------------------------------
          108  +#
          109  +foreach {tn sql} {
          110  +  1 { CREATE VIRTUAL TABLE aa USING fts5vocab() }
          111  +  2 { CREATE VIRTUAL TABLE aa USING fts5vocab(x) }
          112  +  3 { CREATE VIRTUAL TABLE aa USING fts5vocab(x,y,z) }
          113  +  4 { CREATE VIRTUAL TABLE temp.aa USING fts5vocab(x,y,z,y) }
          114  +} {
          115  +  do_catchsql_test 3.$tn $sql {1 {wrong number of vtable arguments}}
          116  +}
    54    117   finish_test
    55    118