/ Check-in [49956395]
Login

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

Overview
Comment:Fix problems with joining two or more fts5_vocab tables that access the same underlying fts5 table.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 49956395e14b61f6bf839e59ae7dd95eb32ebf32f3d16388844de6621b9c2d98
User & Date: dan 2019-01-17 17:39:15
Context
2019-01-17
19:11
Fix a buffer overwrite that could occur when running an fts5 prefix query against a corrupt database. check-in: 3910b563 user: dan tags: trunk
17:39
Fix problems with joining two or more fts5_vocab tables that access the same underlying fts5 table. check-in: 49956395 user: dan tags: trunk
15:40
Revamp the SrcList allocator routines to be methods of Parse instead of being methods of the "sqlite3" object, so that they can leave better error messages when the SrcList object grows too large. check-in: df08d472 user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to ext/fts5/fts5Int.h.

   516    516   
   517    517   /*
   518    518   ** End of interface to code in fts5_varint.c.
   519    519   **************************************************************************/
   520    520   
   521    521   
   522    522   /**************************************************************************
   523         -** Interface to code in fts5.c. 
          523  +** Interface to code in fts5_main.c. 
          524  +*/
          525  +
          526  +/*
          527  +** Virtual-table object.
   524    528   */
          529  +typedef struct Fts5Table Fts5Table;
          530  +struct Fts5Table {
          531  +  sqlite3_vtab base;              /* Base class used by SQLite core */
          532  +  Fts5Config *pConfig;            /* Virtual table configuration */
          533  +  Fts5Index *pIndex;              /* Full-text index */
          534  +};
   525    535   
   526    536   int sqlite3Fts5GetTokenizer(
   527    537     Fts5Global*, 
   528    538     const char **azArg,
   529    539     int nArg,
   530    540     Fts5Tokenizer**,
   531    541     fts5_tokenizer**,
   532    542     char **pzErr
   533    543   );
   534    544   
   535         -Fts5Index *sqlite3Fts5IndexFromCsrid(Fts5Global*, i64, Fts5Config **);
          545  +Fts5Table *sqlite3Fts5TableFromCsrid(Fts5Global*, i64);
          546  +
          547  +int sqlite3Fts5FlushToDisk(Fts5Table*);
   536    548   
   537    549   /*
   538    550   ** End of interface to code in fts5.c.
   539    551   **************************************************************************/
   540    552   
   541    553   /**************************************************************************
   542    554   ** Interface to code in fts5_hash.c. 

Changes to ext/fts5/fts5_main.c.

    24     24   */
    25     25   int sqlite3_fts5_may_be_corrupt = 1;
    26     26   
    27     27   
    28     28   typedef struct Fts5Auxdata Fts5Auxdata;
    29     29   typedef struct Fts5Auxiliary Fts5Auxiliary;
    30     30   typedef struct Fts5Cursor Fts5Cursor;
           31  +typedef struct Fts5FullTable Fts5FullTable;
    31     32   typedef struct Fts5Sorter Fts5Sorter;
    32         -typedef struct Fts5Table Fts5Table;
    33     33   typedef struct Fts5TokenizerModule Fts5TokenizerModule;
    34     34   
    35     35   /*
    36     36   ** NOTES ON TRANSACTIONS: 
    37     37   **
    38     38   ** SQLite invokes the following virtual table methods as transactions are 
    39     39   ** opened and closed by the user:
................................................................................
   106    106     char *zName;                    /* Name of tokenizer */
   107    107     void *pUserData;                /* User pointer passed to xCreate() */
   108    108     fts5_tokenizer x;               /* Tokenizer functions */
   109    109     void (*xDestroy)(void*);        /* Destructor function */
   110    110     Fts5TokenizerModule *pNext;     /* Next registered tokenizer module */
   111    111   };
   112    112   
   113         -/*
   114         -** Virtual-table object.
   115         -*/
   116         -struct Fts5Table {
   117         -  sqlite3_vtab base;              /* Base class used by SQLite core */
   118         -  Fts5Config *pConfig;            /* Virtual table configuration */
   119         -  Fts5Index *pIndex;              /* Full-text index */
          113  +struct Fts5FullTable {
          114  +  Fts5Table p;                    /* Public class members from fts5Int.h */
   120    115     Fts5Storage *pStorage;          /* Document store */
   121    116     Fts5Global *pGlobal;            /* Global (connection wide) data */
   122    117     Fts5Cursor *pSortCsr;           /* Sort data from this cursor */
   123    118   #ifdef SQLITE_DEBUG
   124    119     struct Fts5TransactionState ts;
   125    120   #endif
   126    121   };
................................................................................
   250    245   #define FTS5_BEGIN      1
   251    246   #define FTS5_SYNC       2
   252    247   #define FTS5_COMMIT     3
   253    248   #define FTS5_ROLLBACK   4
   254    249   #define FTS5_SAVEPOINT  5
   255    250   #define FTS5_RELEASE    6
   256    251   #define FTS5_ROLLBACKTO 7
   257         -static void fts5CheckTransactionState(Fts5Table *p, int op, int iSavepoint){
          252  +static void fts5CheckTransactionState(Fts5FullTable *p, int op, int iSavepoint){
   258    253     switch( op ){
   259    254       case FTS5_BEGIN:
   260    255         assert( p->ts.eState==0 );
   261    256         p->ts.eState = 1;
   262    257         p->ts.iSavepoint = -1;
   263    258         break;
   264    259   
................................................................................
   302    297   #else
   303    298   # define fts5CheckTransactionState(x,y,z)
   304    299   #endif
   305    300   
   306    301   /*
   307    302   ** Return true if pTab is a contentless table.
   308    303   */
   309         -static int fts5IsContentless(Fts5Table *pTab){
   310         -  return pTab->pConfig->eContent==FTS5_CONTENT_NONE;
          304  +static int fts5IsContentless(Fts5FullTable *pTab){
          305  +  return pTab->p.pConfig->eContent==FTS5_CONTENT_NONE;
   311    306   }
   312    307   
   313    308   /*
   314    309   ** Delete a virtual table handle allocated by fts5InitVtab(). 
   315    310   */
   316         -static void fts5FreeVtab(Fts5Table *pTab){
          311  +static void fts5FreeVtab(Fts5FullTable *pTab){
   317    312     if( pTab ){
   318         -    sqlite3Fts5IndexClose(pTab->pIndex);
          313  +    sqlite3Fts5IndexClose(pTab->p.pIndex);
   319    314       sqlite3Fts5StorageClose(pTab->pStorage);
   320         -    sqlite3Fts5ConfigFree(pTab->pConfig);
          315  +    sqlite3Fts5ConfigFree(pTab->p.pConfig);
   321    316       sqlite3_free(pTab);
   322    317     }
   323    318   }
   324    319   
   325    320   /*
   326    321   ** The xDisconnect() virtual table method.
   327    322   */
   328    323   static int fts5DisconnectMethod(sqlite3_vtab *pVtab){
   329         -  fts5FreeVtab((Fts5Table*)pVtab);
          324  +  fts5FreeVtab((Fts5FullTable*)pVtab);
   330    325     return SQLITE_OK;
   331    326   }
   332    327   
   333    328   /*
   334    329   ** The xDestroy() virtual table method.
   335    330   */
   336    331   static int fts5DestroyMethod(sqlite3_vtab *pVtab){
   337    332     Fts5Table *pTab = (Fts5Table*)pVtab;
   338    333     int rc = sqlite3Fts5DropAll(pTab->pConfig);
   339    334     if( rc==SQLITE_OK ){
   340         -    fts5FreeVtab((Fts5Table*)pVtab);
          335  +    fts5FreeVtab((Fts5FullTable*)pVtab);
   341    336     }
   342    337     return rc;
   343    338   }
   344    339   
   345    340   /*
   346    341   ** This function is the implementation of both the xConnect and xCreate
   347    342   ** methods of the FTS3 virtual table.
................................................................................
   362    357     sqlite3_vtab **ppVTab,          /* Write the resulting vtab structure here */
   363    358     char **pzErr                    /* Write any error message here */
   364    359   ){
   365    360     Fts5Global *pGlobal = (Fts5Global*)pAux;
   366    361     const char **azConfig = (const char**)argv;
   367    362     int rc = SQLITE_OK;             /* Return code */
   368    363     Fts5Config *pConfig = 0;        /* Results of parsing argc/argv */
   369         -  Fts5Table *pTab = 0;            /* New virtual table object */
          364  +  Fts5FullTable *pTab = 0;        /* New virtual table object */
   370    365   
   371    366     /* Allocate the new vtab object and parse the configuration */
   372         -  pTab = (Fts5Table*)sqlite3Fts5MallocZero(&rc, sizeof(Fts5Table));
          367  +  pTab = (Fts5FullTable*)sqlite3Fts5MallocZero(&rc, sizeof(Fts5FullTable));
   373    368     if( rc==SQLITE_OK ){
   374    369       rc = sqlite3Fts5ConfigParse(pGlobal, db, argc, azConfig, &pConfig, pzErr);
   375    370       assert( (rc==SQLITE_OK && *pzErr==0) || pConfig==0 );
   376    371     }
   377    372     if( rc==SQLITE_OK ){
   378         -    pTab->pConfig = pConfig;
          373  +    pTab->p.pConfig = pConfig;
   379    374       pTab->pGlobal = pGlobal;
   380    375     }
   381    376   
   382    377     /* Open the index sub-system */
   383    378     if( rc==SQLITE_OK ){
   384         -    rc = sqlite3Fts5IndexOpen(pConfig, bCreate, &pTab->pIndex, pzErr);
          379  +    rc = sqlite3Fts5IndexOpen(pConfig, bCreate, &pTab->p.pIndex, pzErr);
   385    380     }
   386    381   
   387    382     /* Open the storage sub-system */
   388    383     if( rc==SQLITE_OK ){
   389    384       rc = sqlite3Fts5StorageOpen(
   390         -        pConfig, pTab->pIndex, bCreate, &pTab->pStorage, pzErr
          385  +        pConfig, pTab->p.pIndex, bCreate, &pTab->pStorage, pzErr
   391    386       );
   392    387     }
   393    388   
   394    389     /* Call sqlite3_declare_vtab() */
   395    390     if( rc==SQLITE_OK ){
   396    391       rc = sqlite3Fts5ConfigDeclareVtab(pConfig);
   397    392     }
   398    393   
   399    394     /* Load the initial configuration */
   400    395     if( rc==SQLITE_OK ){
   401    396       assert( pConfig->pzErrmsg==0 );
   402    397       pConfig->pzErrmsg = pzErr;
   403         -    rc = sqlite3Fts5IndexLoadConfig(pTab->pIndex);
   404         -    sqlite3Fts5IndexRollback(pTab->pIndex);
          398  +    rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex);
          399  +    sqlite3Fts5IndexRollback(pTab->p.pIndex);
   405    400       pConfig->pzErrmsg = 0;
   406    401     }
   407    402   
   408    403     if( rc!=SQLITE_OK ){
   409    404       fts5FreeVtab(pTab);
   410    405       pTab = 0;
   411    406     }else if( bCreate ){
................................................................................
   610    605       }
   611    606     }
   612    607   
   613    608     pInfo->idxNum = idxFlags;
   614    609     return SQLITE_OK;
   615    610   }
   616    611   
   617         -static int fts5NewTransaction(Fts5Table *pTab){
          612  +static int fts5NewTransaction(Fts5FullTable *pTab){
   618    613     Fts5Cursor *pCsr;
   619    614     for(pCsr=pTab->pGlobal->pCsr; pCsr; pCsr=pCsr->pNext){
   620    615       if( pCsr->base.pVtab==(sqlite3_vtab*)pTab ) return SQLITE_OK;
   621    616     }
   622    617     return sqlite3Fts5StorageReset(pTab->pStorage);
   623    618   }
   624    619   
   625    620   /*
   626    621   ** Implementation of xOpen method.
   627    622   */
   628    623   static int fts5OpenMethod(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCsr){
   629         -  Fts5Table *pTab = (Fts5Table*)pVTab;
   630         -  Fts5Config *pConfig = pTab->pConfig;
          624  +  Fts5FullTable *pTab = (Fts5FullTable*)pVTab;
          625  +  Fts5Config *pConfig = pTab->p.pConfig;
   631    626     Fts5Cursor *pCsr = 0;           /* New cursor object */
   632    627     sqlite3_int64 nByte;            /* Bytes of space to allocate */
   633    628     int rc;                         /* Return code */
   634    629   
   635    630     rc = fts5NewTransaction(pTab);
   636    631     if( rc==SQLITE_OK ){
   637    632       nByte = sizeof(Fts5Cursor) + pConfig->nCol * sizeof(int);
................................................................................
   669    664       | FTS5CSR_REQUIRE_DOCSIZE 
   670    665       | FTS5CSR_REQUIRE_INST 
   671    666       | FTS5CSR_REQUIRE_POSLIST 
   672    667     );
   673    668   }
   674    669   
   675    670   static void fts5FreeCursorComponents(Fts5Cursor *pCsr){
   676         -  Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
          671  +  Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab);
   677    672     Fts5Auxdata *pData;
   678    673     Fts5Auxdata *pNext;
   679    674   
   680    675     sqlite3_free(pCsr->aInstIter);
   681    676     sqlite3_free(pCsr->aInst);
   682    677     if( pCsr->pStmt ){
   683    678       int eStmt = fts5StmtType(pCsr);
................................................................................
   713    708   
   714    709   /*
   715    710   ** Close the cursor.  For additional information see the documentation
   716    711   ** on the xClose method of the virtual table interface.
   717    712   */
   718    713   static int fts5CloseMethod(sqlite3_vtab_cursor *pCursor){
   719    714     if( pCursor ){
   720         -    Fts5Table *pTab = (Fts5Table*)(pCursor->pVtab);
          715  +    Fts5FullTable *pTab = (Fts5FullTable*)(pCursor->pVtab);
   721    716       Fts5Cursor *pCsr = (Fts5Cursor*)pCursor;
   722    717       Fts5Cursor **pp;
   723    718   
   724    719       fts5FreeCursorComponents(pCsr);
   725    720       /* Remove the cursor from the Fts5Global.pCsr list */
   726    721       for(pp=&pTab->pGlobal->pCsr; (*pp)!=pCsr; pp=&(*pp)->pNext);
   727    722       *pp = pCsr->pNext;
................................................................................
   770    765   }
   771    766   
   772    767   
   773    768   /*
   774    769   ** Set the FTS5CSR_REQUIRE_RESEEK flag on all FTS5_PLAN_MATCH cursors 
   775    770   ** open on table pTab.
   776    771   */
   777         -static void fts5TripCursors(Fts5Table *pTab){
          772  +static void fts5TripCursors(Fts5FullTable *pTab){
   778    773     Fts5Cursor *pCsr;
   779    774     for(pCsr=pTab->pGlobal->pCsr; pCsr; pCsr=pCsr->pNext){
   780    775       if( pCsr->ePlan==FTS5_PLAN_MATCH
   781    776        && pCsr->base.pVtab==(sqlite3_vtab*)pTab 
   782    777       ){
   783    778         CsrFlagSet(pCsr, FTS5CSR_REQUIRE_RESEEK);
   784    779       }
................................................................................
   797    792   ** Return SQLITE_OK if successful or if no reseek was required, or an 
   798    793   ** error code if an error occurred.
   799    794   */
   800    795   static int fts5CursorReseek(Fts5Cursor *pCsr, int *pbSkip){
   801    796     int rc = SQLITE_OK;
   802    797     assert( *pbSkip==0 );
   803    798     if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_RESEEK) ){
   804         -    Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
          799  +    Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab);
   805    800       int bDesc = pCsr->bDesc;
   806    801       i64 iRowid = sqlite3Fts5ExprRowid(pCsr->pExpr);
   807    802   
   808         -    rc = sqlite3Fts5ExprFirst(pCsr->pExpr, pTab->pIndex, iRowid, bDesc);
          803  +    rc = sqlite3Fts5ExprFirst(pCsr->pExpr, pTab->p.pIndex, iRowid, bDesc);
   809    804       if( rc==SQLITE_OK &&  iRowid!=sqlite3Fts5ExprRowid(pCsr->pExpr) ){
   810    805         *pbSkip = 1;
   811    806       }
   812    807   
   813    808       CsrFlagClear(pCsr, FTS5CSR_REQUIRE_RESEEK);
   814    809       fts5CsrNewrow(pCsr);
   815    810       if( sqlite3Fts5ExprEof(pCsr->pExpr) ){
................................................................................
   898    893     }
   899    894   
   900    895     va_end(ap);
   901    896     *ppStmt = pRet;
   902    897     return rc;
   903    898   } 
   904    899   
   905         -static int fts5CursorFirstSorted(Fts5Table *pTab, Fts5Cursor *pCsr, int bDesc){
   906         -  Fts5Config *pConfig = pTab->pConfig;
          900  +static int fts5CursorFirstSorted(
          901  +  Fts5FullTable *pTab, 
          902  +  Fts5Cursor *pCsr, 
          903  +  int bDesc
          904  +){
          905  +  Fts5Config *pConfig = pTab->p.pConfig;
   907    906     Fts5Sorter *pSorter;
   908    907     int nPhrase;
   909    908     sqlite3_int64 nByte;
   910    909     int rc;
   911    910     const char *zRank = pCsr->zRank;
   912    911     const char *zRankArgs = pCsr->zRankArgs;
   913    912     
................................................................................
   946    945       sqlite3_free(pSorter);
   947    946       pCsr->pSorter = 0;
   948    947     }
   949    948   
   950    949     return rc;
   951    950   }
   952    951   
   953         -static int fts5CursorFirst(Fts5Table *pTab, Fts5Cursor *pCsr, int bDesc){
          952  +static int fts5CursorFirst(Fts5FullTable *pTab, Fts5Cursor *pCsr, int bDesc){
   954    953     int rc;
   955    954     Fts5Expr *pExpr = pCsr->pExpr;
   956         -  rc = sqlite3Fts5ExprFirst(pExpr, pTab->pIndex, pCsr->iFirstRowid, bDesc);
          955  +  rc = sqlite3Fts5ExprFirst(pExpr, pTab->p.pIndex, pCsr->iFirstRowid, bDesc);
   957    956     if( sqlite3Fts5ExprEof(pExpr) ){
   958    957       CsrFlagSet(pCsr, FTS5CSR_EOF);
   959    958     }
   960    959     fts5CsrNewrow(pCsr);
   961    960     return rc;
   962    961   }
   963    962   
................................................................................
   964    963   /*
   965    964   ** Process a "special" query. A special query is identified as one with a
   966    965   ** MATCH expression that begins with a '*' character. The remainder of
   967    966   ** the text passed to the MATCH operator are used as  the special query
   968    967   ** parameters.
   969    968   */
   970    969   static int fts5SpecialMatch(
   971         -  Fts5Table *pTab, 
          970  +  Fts5FullTable *pTab, 
   972    971     Fts5Cursor *pCsr, 
   973    972     const char *zQuery
   974    973   ){
   975    974     int rc = SQLITE_OK;             /* Return code */
   976    975     const char *z = zQuery;         /* Special query text */
   977    976     int n;                          /* Number of bytes in text at z */
   978    977   
   979    978     while( z[0]==' ' ) z++;
   980    979     for(n=0; z[n] && z[n]!=' '; n++);
   981    980   
   982         -  assert( pTab->base.zErrMsg==0 );
          981  +  assert( pTab->p.base.zErrMsg==0 );
   983    982     pCsr->ePlan = FTS5_PLAN_SPECIAL;
   984    983   
   985    984     if( 0==sqlite3_strnicmp("reads", z, n) ){
   986         -    pCsr->iSpecial = sqlite3Fts5IndexReads(pTab->pIndex);
          985  +    pCsr->iSpecial = sqlite3Fts5IndexReads(pTab->p.pIndex);
   987    986     }
   988    987     else if( 0==sqlite3_strnicmp("id", z, n) ){
   989    988       pCsr->iSpecial = pCsr->iCsrId;
   990    989     }
   991    990     else{
   992    991       /* An unrecognized directive. Return an error message. */
   993         -    pTab->base.zErrMsg = sqlite3_mprintf("unknown special query: %.*s", n, z);
          992  +    pTab->p.base.zErrMsg = sqlite3_mprintf("unknown special query: %.*s", n, z);
   994    993       rc = SQLITE_ERROR;
   995    994     }
   996    995   
   997    996     return rc;
   998    997   }
   999    998   
  1000    999   /*
  1001   1000   ** Search for an auxiliary function named zName that can be used with table
  1002   1001   ** pTab. If one is found, return a pointer to the corresponding Fts5Auxiliary
  1003   1002   ** structure. Otherwise, if no such function exists, return NULL.
  1004   1003   */
  1005         -static Fts5Auxiliary *fts5FindAuxiliary(Fts5Table *pTab, const char *zName){
         1004  +static Fts5Auxiliary *fts5FindAuxiliary(Fts5FullTable *pTab, const char *zName){
  1006   1005     Fts5Auxiliary *pAux;
  1007   1006   
  1008   1007     for(pAux=pTab->pGlobal->pAux; pAux; pAux=pAux->pNext){
  1009   1008       if( sqlite3_stricmp(zName, pAux->zFunc)==0 ) return pAux;
  1010   1009     }
  1011   1010   
  1012   1011     /* No function of the specified name was found. Return 0. */
  1013   1012     return 0;
  1014   1013   }
  1015   1014   
  1016   1015   
  1017   1016   static int fts5FindRankFunction(Fts5Cursor *pCsr){
  1018         -  Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
  1019         -  Fts5Config *pConfig = pTab->pConfig;
         1017  +  Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab);
         1018  +  Fts5Config *pConfig = pTab->p.pConfig;
  1020   1019     int rc = SQLITE_OK;
  1021   1020     Fts5Auxiliary *pAux = 0;
  1022   1021     const char *zRank = pCsr->zRank;
  1023   1022     const char *zRankArgs = pCsr->zRankArgs;
  1024   1023   
  1025   1024     if( zRankArgs ){
  1026   1025       char *zSql = sqlite3Fts5Mprintf(&rc, "SELECT %s", zRankArgs);
................................................................................
  1050   1049         }
  1051   1050       }
  1052   1051     }
  1053   1052   
  1054   1053     if( rc==SQLITE_OK ){
  1055   1054       pAux = fts5FindAuxiliary(pTab, zRank);
  1056   1055       if( pAux==0 ){
  1057         -      assert( pTab->base.zErrMsg==0 );
  1058         -      pTab->base.zErrMsg = sqlite3_mprintf("no such function: %s", zRank);
         1056  +      assert( pTab->p.base.zErrMsg==0 );
         1057  +      pTab->p.base.zErrMsg = sqlite3_mprintf("no such function: %s", zRank);
  1059   1058         rc = SQLITE_ERROR;
  1060   1059       }
  1061   1060     }
  1062   1061   
  1063   1062     pCsr->pRank = pAux;
  1064   1063     return rc;
  1065   1064   }
................................................................................
  1126   1125   static int fts5FilterMethod(
  1127   1126     sqlite3_vtab_cursor *pCursor,   /* The cursor used for this query */
  1128   1127     int idxNum,                     /* Strategy index */
  1129   1128     const char *zUnused,            /* Unused */
  1130   1129     int nVal,                       /* Number of elements in apVal */
  1131   1130     sqlite3_value **apVal           /* Arguments for the indexing scheme */
  1132   1131   ){
  1133         -  Fts5Table *pTab = (Fts5Table*)(pCursor->pVtab);
  1134         -  Fts5Config *pConfig = pTab->pConfig;
         1132  +  Fts5FullTable *pTab = (Fts5FullTable*)(pCursor->pVtab);
         1133  +  Fts5Config *pConfig = pTab->p.pConfig;
  1135   1134     Fts5Cursor *pCsr = (Fts5Cursor*)pCursor;
  1136   1135     int rc = SQLITE_OK;             /* Error code */
  1137   1136     int iVal = 0;                   /* Counter for apVal[] */
  1138   1137     int bDesc;                      /* True if ORDER BY [rank|rowid] DESC */
  1139   1138     int bOrderByRank;               /* True if ORDER BY rank */
  1140   1139     sqlite3_value *pMatch = 0;      /* <tbl> MATCH ? expression (or NULL) */
  1141   1140     sqlite3_value *pRank = 0;       /* rank MATCH ? expression (or NULL) */
................................................................................
  1156   1155     assert( pCsr->pStmt==0 );
  1157   1156     assert( pCsr->pExpr==0 );
  1158   1157     assert( pCsr->csrflags==0 );
  1159   1158     assert( pCsr->pRank==0 );
  1160   1159     assert( pCsr->zRank==0 );
  1161   1160     assert( pCsr->zRankArgs==0 );
  1162   1161   
  1163         -  assert( pzErrmsg==0 || pzErrmsg==&pTab->base.zErrMsg );
  1164         -  pConfig->pzErrmsg = &pTab->base.zErrMsg;
         1162  +  assert( pzErrmsg==0 || pzErrmsg==&pTab->p.base.zErrMsg );
         1163  +  pConfig->pzErrmsg = &pTab->p.base.zErrMsg;
  1165   1164   
  1166   1165     /* Decode the arguments passed through to this function.
  1167   1166     **
  1168   1167     ** Note: The following set of if(...) statements must be in the same
  1169   1168     ** order as the corresponding entries in the struct at the top of
  1170   1169     ** fts5BestIndexMethod().  */
  1171   1170     if( BitFlagTest(idxNum, FTS5_BI_MATCH) ) pMatch = apVal[iVal++];
................................................................................
  1223   1222       if( rc==SQLITE_OK ){
  1224   1223         if( zExpr[0]=='*' ){
  1225   1224           /* The user has issued a query of the form "MATCH '*...'". This
  1226   1225           ** indicates that the MATCH expression is not a full text query,
  1227   1226           ** but a request for an internal parameter.  */
  1228   1227           rc = fts5SpecialMatch(pTab, pCsr, &zExpr[1]);
  1229   1228         }else{
  1230         -        char **pzErr = &pTab->base.zErrMsg;
         1229  +        char **pzErr = &pTab->p.base.zErrMsg;
  1231   1230           rc = sqlite3Fts5ExprNew(pConfig, iCol, zExpr, &pCsr->pExpr, pzErr);
  1232   1231           if( rc==SQLITE_OK ){
  1233   1232             if( bOrderByRank ){
  1234   1233               pCsr->ePlan = FTS5_PLAN_SORTED_MATCH;
  1235   1234               rc = fts5CursorFirstSorted(pTab, pCsr, bDesc);
  1236   1235             }else{
  1237   1236               pCsr->ePlan = FTS5_PLAN_MATCH;
................................................................................
  1246   1245       );
  1247   1246       rc = SQLITE_ERROR;
  1248   1247     }else{
  1249   1248       /* This is either a full-table scan (ePlan==FTS5_PLAN_SCAN) or a lookup
  1250   1249       ** by rowid (ePlan==FTS5_PLAN_ROWID).  */
  1251   1250       pCsr->ePlan = (pRowidEq ? FTS5_PLAN_ROWID : FTS5_PLAN_SCAN);
  1252   1251       rc = sqlite3Fts5StorageStmt(
  1253         -        pTab->pStorage, fts5StmtType(pCsr), &pCsr->pStmt, &pTab->base.zErrMsg
         1252  +        pTab->pStorage, fts5StmtType(pCsr), &pCsr->pStmt, &pTab->p.base.zErrMsg
  1254   1253       );
  1255   1254       if( rc==SQLITE_OK ){
  1256   1255         if( pCsr->ePlan==FTS5_PLAN_ROWID ){
  1257   1256           sqlite3_bind_value(pCsr->pStmt, 1, apVal[0]);
  1258   1257         }else{
  1259   1258           sqlite3_bind_int64(pCsr->pStmt, 1, pCsr->iFirstRowid);
  1260   1259           sqlite3_bind_int64(pCsr->pStmt, 2, pCsr->iLastRowid);
................................................................................
  1329   1328   ** be left in sqlite3_vtab.zErrMsg.
  1330   1329   */
  1331   1330   static int fts5SeekCursor(Fts5Cursor *pCsr, int bErrormsg){
  1332   1331     int rc = SQLITE_OK;
  1333   1332   
  1334   1333     /* If the cursor does not yet have a statement handle, obtain one now. */ 
  1335   1334     if( pCsr->pStmt==0 ){
  1336         -    Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
         1335  +    Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab);
  1337   1336       int eStmt = fts5StmtType(pCsr);
  1338   1337       rc = sqlite3Fts5StorageStmt(
  1339         -        pTab->pStorage, eStmt, &pCsr->pStmt, (bErrormsg?&pTab->base.zErrMsg:0)
         1338  +        pTab->pStorage, eStmt, &pCsr->pStmt, (bErrormsg?&pTab->p.base.zErrMsg:0)
  1340   1339       );
  1341         -    assert( rc!=SQLITE_OK || pTab->base.zErrMsg==0 );
         1340  +    assert( rc!=SQLITE_OK || pTab->p.base.zErrMsg==0 );
  1342   1341       assert( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_CONTENT) );
  1343   1342     }
  1344   1343   
  1345   1344     if( rc==SQLITE_OK && CsrFlagTest(pCsr, FTS5CSR_REQUIRE_CONTENT) ){
  1346   1345       assert( pCsr->pExpr );
  1347   1346       sqlite3_reset(pCsr->pStmt);
  1348   1347       sqlite3_bind_int64(pCsr->pStmt, 1, fts5CursorRowid(pCsr));
................................................................................
  1356   1355           rc = FTS5_CORRUPT;
  1357   1356         }
  1358   1357       }
  1359   1358     }
  1360   1359     return rc;
  1361   1360   }
  1362   1361   
  1363         -static void fts5SetVtabError(Fts5Table *p, const char *zFormat, ...){
         1362  +static void fts5SetVtabError(Fts5FullTable *p, const char *zFormat, ...){
  1364   1363     va_list ap;                     /* ... printf arguments */
  1365   1364     va_start(ap, zFormat);
  1366         -  assert( p->base.zErrMsg==0 );
  1367         -  p->base.zErrMsg = sqlite3_vmprintf(zFormat, ap);
         1365  +  assert( p->p.base.zErrMsg==0 );
         1366  +  p->p.base.zErrMsg = sqlite3_vmprintf(zFormat, ap);
  1368   1367     va_end(ap);
  1369   1368   }
  1370   1369   
  1371   1370   /*
  1372   1371   ** This function is called to handle an FTS INSERT command. In other words,
  1373   1372   ** an INSERT statement of the form:
  1374   1373   **
................................................................................
  1380   1379   ** error code if an error occurs.
  1381   1380   **
  1382   1381   ** The commands implemented by this function are documented in the "Special
  1383   1382   ** INSERT Directives" section of the documentation. It should be updated if
  1384   1383   ** more commands are added to this function.
  1385   1384   */
  1386   1385   static int fts5SpecialInsert(
  1387         -  Fts5Table *pTab,                /* Fts5 table object */
         1386  +  Fts5FullTable *pTab,            /* Fts5 table object */
  1388   1387     const char *zCmd,               /* Text inserted into table-name column */
  1389   1388     sqlite3_value *pVal             /* Value inserted into rank column */
  1390   1389   ){
  1391         -  Fts5Config *pConfig = pTab->pConfig;
         1390  +  Fts5Config *pConfig = pTab->p.pConfig;
  1392   1391     int rc = SQLITE_OK;
  1393   1392     int bError = 0;
  1394   1393   
  1395   1394     if( 0==sqlite3_stricmp("delete-all", zCmd) ){
  1396   1395       if( pConfig->eContent==FTS5_CONTENT_NORMAL ){
  1397   1396         fts5SetVtabError(pTab, 
  1398   1397             "'delete-all' may only be used with a "
................................................................................
  1419   1418     }else if( 0==sqlite3_stricmp("integrity-check", zCmd) ){
  1420   1419       rc = sqlite3Fts5StorageIntegrity(pTab->pStorage);
  1421   1420   #ifdef SQLITE_DEBUG
  1422   1421     }else if( 0==sqlite3_stricmp("prefix-index", zCmd) ){
  1423   1422       pConfig->bPrefixIndex = sqlite3_value_int(pVal);
  1424   1423   #endif
  1425   1424     }else{
  1426         -    rc = sqlite3Fts5IndexLoadConfig(pTab->pIndex);
         1425  +    rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex);
  1427   1426       if( rc==SQLITE_OK ){
  1428         -      rc = sqlite3Fts5ConfigSetValue(pTab->pConfig, zCmd, pVal, &bError);
         1427  +      rc = sqlite3Fts5ConfigSetValue(pTab->p.pConfig, zCmd, pVal, &bError);
  1429   1428       }
  1430   1429       if( rc==SQLITE_OK ){
  1431   1430         if( bError ){
  1432   1431           rc = SQLITE_ERROR;
  1433   1432         }else{
  1434   1433           rc = sqlite3Fts5StorageConfigValue(pTab->pStorage, zCmd, pVal, 0);
  1435   1434         }
  1436   1435       }
  1437   1436     }
  1438   1437     return rc;
  1439   1438   }
  1440   1439   
  1441   1440   static int fts5SpecialDelete(
  1442         -  Fts5Table *pTab, 
         1441  +  Fts5FullTable *pTab, 
  1443   1442     sqlite3_value **apVal
  1444   1443   ){
  1445   1444     int rc = SQLITE_OK;
  1446   1445     int eType1 = sqlite3_value_type(apVal[1]);
  1447   1446     if( eType1==SQLITE_INTEGER ){
  1448   1447       sqlite3_int64 iDel = sqlite3_value_int64(apVal[1]);
  1449   1448       rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel, &apVal[2]);
  1450   1449     }
  1451   1450     return rc;
  1452   1451   }
  1453   1452   
  1454   1453   static void fts5StorageInsert(
  1455   1454     int *pRc, 
  1456         -  Fts5Table *pTab, 
         1455  +  Fts5FullTable *pTab, 
  1457   1456     sqlite3_value **apVal, 
  1458   1457     i64 *piRowid
  1459   1458   ){
  1460   1459     int rc = *pRc;
  1461   1460     if( rc==SQLITE_OK ){
  1462   1461       rc = sqlite3Fts5StorageContentInsert(pTab->pStorage, apVal, piRowid);
  1463   1462     }
................................................................................
  1483   1482   */
  1484   1483   static int fts5UpdateMethod(
  1485   1484     sqlite3_vtab *pVtab,            /* Virtual table handle */
  1486   1485     int nArg,                       /* Size of argument array */
  1487   1486     sqlite3_value **apVal,          /* Array of arguments */
  1488   1487     sqlite_int64 *pRowid            /* OUT: The affected (or effected) rowid */
  1489   1488   ){
  1490         -  Fts5Table *pTab = (Fts5Table*)pVtab;
  1491         -  Fts5Config *pConfig = pTab->pConfig;
         1489  +  Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
         1490  +  Fts5Config *pConfig = pTab->p.pConfig;
  1492   1491     int eType0;                     /* value_type() of apVal[0] */
  1493   1492     int rc = SQLITE_OK;             /* Return code */
  1494   1493   
  1495   1494     /* A transaction must be open when this is called. */
  1496   1495     assert( pTab->ts.eState==1 );
  1497   1496   
  1498   1497     assert( pVtab->zErrMsg==0 );
  1499   1498     assert( nArg==1 || nArg==(2+pConfig->nCol+2) );
  1500   1499     assert( sqlite3_value_type(apVal[0])==SQLITE_INTEGER 
  1501   1500          || sqlite3_value_type(apVal[0])==SQLITE_NULL 
  1502   1501     );
  1503         -  assert( pTab->pConfig->pzErrmsg==0 );
  1504         -  pTab->pConfig->pzErrmsg = &pTab->base.zErrMsg;
         1502  +  assert( pTab->p.pConfig->pzErrmsg==0 );
         1503  +  pTab->p.pConfig->pzErrmsg = &pTab->p.base.zErrMsg;
  1505   1504   
  1506   1505     /* Put any active cursors into REQUIRE_SEEK state. */
  1507   1506     fts5TripCursors(pTab);
  1508   1507   
  1509   1508     eType0 = sqlite3_value_type(apVal[0]);
  1510   1509     if( eType0==SQLITE_NULL 
  1511   1510      && sqlite3_value_type(apVal[2+pConfig->nCol])!=SQLITE_NULL 
................................................................................
  1538   1537   
  1539   1538       assert( eType0==SQLITE_INTEGER || eType0==SQLITE_NULL );
  1540   1539       assert( nArg!=1 || eType0==SQLITE_INTEGER );
  1541   1540   
  1542   1541       /* Filter out attempts to run UPDATE or DELETE on contentless tables.
  1543   1542       ** This is not suported.  */
  1544   1543       if( eType0==SQLITE_INTEGER && fts5IsContentless(pTab) ){
  1545         -      pTab->base.zErrMsg = sqlite3_mprintf(
         1544  +      pTab->p.base.zErrMsg = sqlite3_mprintf(
  1546   1545             "cannot %s contentless fts5 table: %s", 
  1547   1546             (nArg>1 ? "UPDATE" : "DELETE from"), pConfig->zName
  1548   1547         );
  1549   1548         rc = SQLITE_ERROR;
  1550   1549       }
  1551   1550   
  1552   1551       /* DELETE */
................................................................................
  1596   1595             rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0);
  1597   1596             fts5StorageInsert(&rc, pTab, apVal, pRowid);
  1598   1597           }
  1599   1598         }
  1600   1599       }
  1601   1600     }
  1602   1601   
  1603         -  pTab->pConfig->pzErrmsg = 0;
         1602  +  pTab->p.pConfig->pzErrmsg = 0;
  1604   1603     return rc;
  1605   1604   }
  1606   1605   
  1607   1606   /*
  1608   1607   ** Implementation of xSync() method. 
  1609   1608   */
  1610   1609   static int fts5SyncMethod(sqlite3_vtab *pVtab){
  1611   1610     int rc;
  1612         -  Fts5Table *pTab = (Fts5Table*)pVtab;
         1611  +  Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
  1613   1612     fts5CheckTransactionState(pTab, FTS5_SYNC, 0);
  1614         -  pTab->pConfig->pzErrmsg = &pTab->base.zErrMsg;
         1613  +  pTab->p.pConfig->pzErrmsg = &pTab->p.base.zErrMsg;
  1615   1614     fts5TripCursors(pTab);
  1616   1615     rc = sqlite3Fts5StorageSync(pTab->pStorage);
  1617         -  pTab->pConfig->pzErrmsg = 0;
         1616  +  pTab->p.pConfig->pzErrmsg = 0;
  1618   1617     return rc;
  1619   1618   }
  1620   1619   
  1621   1620   /*
  1622   1621   ** Implementation of xBegin() method. 
  1623   1622   */
  1624   1623   static int fts5BeginMethod(sqlite3_vtab *pVtab){
  1625         -  fts5CheckTransactionState((Fts5Table*)pVtab, FTS5_BEGIN, 0);
  1626         -  fts5NewTransaction((Fts5Table*)pVtab);
         1624  +  fts5CheckTransactionState((Fts5FullTable*)pVtab, FTS5_BEGIN, 0);
         1625  +  fts5NewTransaction((Fts5FullTable*)pVtab);
  1627   1626     return SQLITE_OK;
  1628   1627   }
  1629   1628   
  1630   1629   /*
  1631   1630   ** Implementation of xCommit() method. This is a no-op. The contents of
  1632   1631   ** the pending-terms hash-table have already been flushed into the database
  1633   1632   ** by fts5SyncMethod().
  1634   1633   */
  1635   1634   static int fts5CommitMethod(sqlite3_vtab *pVtab){
  1636   1635     UNUSED_PARAM(pVtab);  /* Call below is a no-op for NDEBUG builds */
  1637         -  fts5CheckTransactionState((Fts5Table*)pVtab, FTS5_COMMIT, 0);
         1636  +  fts5CheckTransactionState((Fts5FullTable*)pVtab, FTS5_COMMIT, 0);
  1638   1637     return SQLITE_OK;
  1639   1638   }
  1640   1639   
  1641   1640   /*
  1642   1641   ** Implementation of xRollback(). Discard the contents of the pending-terms
  1643   1642   ** hash-table. Any changes made to the database are reverted by SQLite.
  1644   1643   */
  1645   1644   static int fts5RollbackMethod(sqlite3_vtab *pVtab){
  1646   1645     int rc;
  1647         -  Fts5Table *pTab = (Fts5Table*)pVtab;
         1646  +  Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
  1648   1647     fts5CheckTransactionState(pTab, FTS5_ROLLBACK, 0);
  1649   1648     rc = sqlite3Fts5StorageRollback(pTab->pStorage);
  1650   1649     return rc;
  1651   1650   }
  1652   1651   
  1653   1652   static int fts5CsrPoslist(Fts5Cursor*, int, const u8**, int*);
  1654   1653   
................................................................................
  1664   1663   
  1665   1664   static int fts5ApiColumnTotalSize(
  1666   1665     Fts5Context *pCtx, 
  1667   1666     int iCol, 
  1668   1667     sqlite3_int64 *pnToken
  1669   1668   ){
  1670   1669     Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
  1671         -  Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
         1670  +  Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab);
  1672   1671     return sqlite3Fts5StorageSize(pTab->pStorage, iCol, pnToken);
  1673   1672   }
  1674   1673   
  1675   1674   static int fts5ApiRowCount(Fts5Context *pCtx, i64 *pnRow){
  1676   1675     Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
  1677         -  Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
         1676  +  Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab);
  1678   1677     return sqlite3Fts5StorageRowCount(pTab->pStorage, pnRow);
  1679   1678   }
  1680   1679   
  1681   1680   static int fts5ApiTokenize(
  1682   1681     Fts5Context *pCtx, 
  1683   1682     const char *pText, int nText, 
  1684   1683     void *pUserData,
................................................................................
  1705   1704     Fts5Context *pCtx, 
  1706   1705     int iCol, 
  1707   1706     const char **pz, 
  1708   1707     int *pn
  1709   1708   ){
  1710   1709     int rc = SQLITE_OK;
  1711   1710     Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
  1712         -  if( fts5IsContentless((Fts5Table*)(pCsr->base.pVtab)) ){
         1711  +  if( fts5IsContentless((Fts5FullTable*)(pCsr->base.pVtab)) ){
  1713   1712       *pz = 0;
  1714   1713       *pn = 0;
  1715   1714     }else{
  1716   1715       rc = fts5SeekCursor(pCsr, 0);
  1717   1716       if( rc==SQLITE_OK ){
  1718   1717         *pz = (const char*)sqlite3_column_text(pCsr->pStmt, iCol+1);
  1719   1718         *pn = sqlite3_column_bytes(pCsr->pStmt, iCol+1);
................................................................................
  1899   1898       (*pCnt)++;
  1900   1899     }
  1901   1900     return SQLITE_OK;
  1902   1901   }
  1903   1902   
  1904   1903   static int fts5ApiColumnSize(Fts5Context *pCtx, int iCol, int *pnToken){
  1905   1904     Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
  1906         -  Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
  1907         -  Fts5Config *pConfig = pTab->pConfig;
         1905  +  Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab);
         1906  +  Fts5Config *pConfig = pTab->p.pConfig;
  1908   1907     int rc = SQLITE_OK;
  1909   1908   
  1910   1909     if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_DOCSIZE) ){
  1911   1910       if( pConfig->bColumnsize ){
  1912   1911         i64 iRowid = fts5CursorRowid(pCsr);
  1913   1912         rc = sqlite3Fts5StorageDocsize(pTab->pStorage, iRowid, pCsr->aColumnSize);
  1914   1913       }else if( pConfig->zContent==0 ){
................................................................................
  2156   2155   static int fts5ApiQueryPhrase(
  2157   2156     Fts5Context *pCtx, 
  2158   2157     int iPhrase, 
  2159   2158     void *pUserData,
  2160   2159     int(*xCallback)(const Fts5ExtensionApi*, Fts5Context*, void*)
  2161   2160   ){
  2162   2161     Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
  2163         -  Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
         2162  +  Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab);
  2164   2163     int rc;
  2165   2164     Fts5Cursor *pNew = 0;
  2166   2165   
  2167   2166     rc = fts5OpenMethod(pCsr->base.pVtab, (sqlite3_vtab_cursor**)&pNew);
  2168   2167     if( rc==SQLITE_OK ){
  2169   2168       pNew->ePlan = FTS5_PLAN_MATCH;
  2170   2169       pNew->iFirstRowid = SMALLEST_INT64;
................................................................................
  2233   2232     }else{
  2234   2233       fts5ApiInvoke(pAux, pCsr, context, argc-1, &argv[1]);
  2235   2234     }
  2236   2235   }
  2237   2236   
  2238   2237   
  2239   2238   /*
  2240         -** Given cursor id iId, return a pointer to the corresponding Fts5Index 
         2239  +** Given cursor id iId, return a pointer to the corresponding Fts5Table 
  2241   2240   ** object. Or NULL If the cursor id does not exist.
  2242         -**
  2243         -** If successful, set *ppConfig to point to the associated config object 
  2244         -** before returning.
  2245   2241   */
  2246         -Fts5Index *sqlite3Fts5IndexFromCsrid(
         2242  +Fts5Table *sqlite3Fts5TableFromCsrid(
  2247   2243     Fts5Global *pGlobal,            /* FTS5 global context for db handle */
  2248         -  i64 iCsrId,                     /* Id of cursor to find */
  2249         -  Fts5Config **ppConfig           /* OUT: Configuration object */
         2244  +  i64 iCsrId                      /* Id of cursor to find */
  2250   2245   ){
  2251   2246     Fts5Cursor *pCsr;
  2252   2247     pCsr = fts5CursorFromCsrid(pGlobal, iCsrId);
  2253   2248     if( pCsr ){
  2254         -    Fts5Table *pTab = (Fts5Table*)pCsr->base.pVtab;
  2255         -    *ppConfig = pTab->pConfig;
  2256         -    return pTab->pIndex;
         2249  +    return (Fts5Table*)pCsr->base.pVtab;
  2257   2250     }
  2258   2251     return 0;
  2259   2252   }
  2260   2253   
  2261   2254   /*
  2262   2255   ** Return a "position-list blob" corresponding to the current position of
  2263   2256   ** cursor pCsr via sqlite3_result_blob(). A position-list blob contains
................................................................................
  2331   2324   ** the row that the supplied cursor currently points to.
  2332   2325   */
  2333   2326   static int fts5ColumnMethod(
  2334   2327     sqlite3_vtab_cursor *pCursor,   /* Cursor to retrieve value from */
  2335   2328     sqlite3_context *pCtx,          /* Context for sqlite3_result_xxx() calls */
  2336   2329     int iCol                        /* Index of column to read value from */
  2337   2330   ){
  2338         -  Fts5Table *pTab = (Fts5Table*)(pCursor->pVtab);
  2339         -  Fts5Config *pConfig = pTab->pConfig;
         2331  +  Fts5FullTable *pTab = (Fts5FullTable*)(pCursor->pVtab);
         2332  +  Fts5Config *pConfig = pTab->p.pConfig;
  2340   2333     Fts5Cursor *pCsr = (Fts5Cursor*)pCursor;
  2341   2334     int rc = SQLITE_OK;
  2342   2335     
  2343   2336     assert( CsrFlagTest(pCsr, FTS5CSR_EOF)==0 );
  2344   2337   
  2345   2338     if( pCsr->ePlan==FTS5_PLAN_SPECIAL ){
  2346   2339       if( iCol==pConfig->nCol ){
................................................................................
  2384   2377   static int fts5FindFunctionMethod(
  2385   2378     sqlite3_vtab *pVtab,            /* Virtual table handle */
  2386   2379     int nUnused,                    /* Number of SQL function arguments */
  2387   2380     const char *zName,              /* Name of SQL function */
  2388   2381     void (**pxFunc)(sqlite3_context*,int,sqlite3_value**), /* OUT: Result */
  2389   2382     void **ppArg                    /* OUT: User data for *pxFunc */
  2390   2383   ){
  2391         -  Fts5Table *pTab = (Fts5Table*)pVtab;
         2384  +  Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
  2392   2385     Fts5Auxiliary *pAux;
  2393   2386   
  2394   2387     UNUSED_PARAM(nUnused);
  2395   2388     pAux = fts5FindAuxiliary(pTab, zName);
  2396   2389     if( pAux ){
  2397   2390       *pxFunc = fts5ApiCallback;
  2398   2391       *ppArg = (void*)pAux;
................................................................................
  2406   2399   /*
  2407   2400   ** Implementation of FTS5 xRename method. Rename an fts5 table.
  2408   2401   */
  2409   2402   static int fts5RenameMethod(
  2410   2403     sqlite3_vtab *pVtab,            /* Virtual table handle */
  2411   2404     const char *zName               /* New name of table */
  2412   2405   ){
  2413         -  Fts5Table *pTab = (Fts5Table*)pVtab;
         2406  +  Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
  2414   2407     return sqlite3Fts5StorageRename(pTab->pStorage, zName);
  2415   2408   }
         2409  +
         2410  +int sqlite3Fts5FlushToDisk(Fts5Table *pTab){
         2411  +  fts5TripCursors((Fts5FullTable*)pTab);
         2412  +  return sqlite3Fts5StorageSync(((Fts5FullTable*)pTab)->pStorage);
         2413  +}
  2416   2414   
  2417   2415   /*
  2418   2416   ** The xSavepoint() method.
  2419   2417   **
  2420   2418   ** Flush the contents of the pending-terms table to disk.
  2421   2419   */
  2422   2420   static int fts5SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){
  2423         -  Fts5Table *pTab = (Fts5Table*)pVtab;
  2424   2421     UNUSED_PARAM(iSavepoint);  /* Call below is a no-op for NDEBUG builds */
  2425         -  fts5CheckTransactionState(pTab, FTS5_SAVEPOINT, iSavepoint);
  2426         -  fts5TripCursors(pTab);
  2427         -  return sqlite3Fts5StorageSync(pTab->pStorage);
         2422  +  fts5CheckTransactionState((Fts5FullTable*)pVtab, FTS5_SAVEPOINT, iSavepoint);
         2423  +  return sqlite3Fts5FlushToDisk((Fts5Table*)pVtab);
  2428   2424   }
  2429   2425   
  2430   2426   /*
  2431   2427   ** The xRelease() method.
  2432   2428   **
  2433   2429   ** This is a no-op.
  2434   2430   */
  2435   2431   static int fts5ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){
  2436         -  Fts5Table *pTab = (Fts5Table*)pVtab;
  2437   2432     UNUSED_PARAM(iSavepoint);  /* Call below is a no-op for NDEBUG builds */
  2438         -  fts5CheckTransactionState(pTab, FTS5_RELEASE, iSavepoint);
  2439         -  fts5TripCursors(pTab);
  2440         -  return sqlite3Fts5StorageSync(pTab->pStorage);
         2433  +  fts5CheckTransactionState((Fts5FullTable*)pVtab, FTS5_RELEASE, iSavepoint);
         2434  +  return sqlite3Fts5FlushToDisk((Fts5Table*)pVtab);
  2441   2435   }
  2442   2436   
  2443   2437   /*
  2444   2438   ** The xRollbackTo() method.
  2445   2439   **
  2446   2440   ** Discard the contents of the pending terms table.
  2447   2441   */
  2448   2442   static int fts5RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){
  2449         -  Fts5Table *pTab = (Fts5Table*)pVtab;
         2443  +  Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
  2450   2444     UNUSED_PARAM(iSavepoint);  /* Call below is a no-op for NDEBUG builds */
  2451   2445     fts5CheckTransactionState(pTab, FTS5_ROLLBACKTO, iSavepoint);
  2452   2446     fts5TripCursors(pTab);
  2453   2447     return sqlite3Fts5StorageRollback(pTab->pStorage);
  2454   2448   }
  2455   2449   
  2456   2450   /*

Changes to ext/fts5/fts5_vocab.c.

    51     51     Fts5Global *pGlobal;            /* FTS5 global object for this database */
    52     52     int eType;                      /* FTS5_VOCAB_COL, ROW or INSTANCE */
    53     53   };
    54     54   
    55     55   struct Fts5VocabCursor {
    56     56     sqlite3_vtab_cursor base;
    57     57     sqlite3_stmt *pStmt;            /* Statement holding lock on pIndex */
    58         -  Fts5Index *pIndex;              /* Associated FTS5 index */
           58  +  Fts5Table *pFts5;               /* Associated FTS5 table */
    59     59   
    60     60     int bEof;                       /* True if this cursor is at EOF */
    61     61     Fts5IndexIter *pIter;           /* Term/rowid iterator object */
    62     62   
    63     63     int nLeTerm;                    /* Size of zLeTerm in bytes */
    64     64     char *zLeTerm;                  /* (term <= $zLeTerm) paramater, or NULL */
    65     65   
    66     66     /* These are used by 'col' tables only */
    67         -  Fts5Config *pConfig;            /* Fts5 table configuration */
    68     67     int iCol;
    69     68     i64 *aCnt;
    70     69     i64 *aDoc;
    71     70   
    72     71     /* Output values used by all tables. */
    73     72     i64 rowid;                      /* This table's current rowid value */
    74     73     Fts5Buffer term;                /* Current value of 'term' column */
................................................................................
   323    322   ** Implementation of xOpen method.
   324    323   */
   325    324   static int fts5VocabOpenMethod(
   326    325     sqlite3_vtab *pVTab, 
   327    326     sqlite3_vtab_cursor **ppCsr
   328    327   ){
   329    328     Fts5VocabTable *pTab = (Fts5VocabTable*)pVTab;
   330         -  Fts5Index *pIndex = 0;
   331         -  Fts5Config *pConfig = 0;
          329  +  Fts5Table *pFts5 = 0;
   332    330     Fts5VocabCursor *pCsr = 0;
   333    331     int rc = SQLITE_OK;
   334    332     sqlite3_stmt *pStmt = 0;
   335    333     char *zSql = 0;
   336    334   
   337    335     zSql = sqlite3Fts5Mprintf(&rc,
   338    336         "SELECT t.%Q FROM %Q.%Q AS t WHERE t.%Q MATCH '*id'",
................................................................................
   343    341     }
   344    342     sqlite3_free(zSql);
   345    343     assert( rc==SQLITE_OK || pStmt==0 );
   346    344     if( rc==SQLITE_ERROR ) rc = SQLITE_OK;
   347    345   
   348    346     if( pStmt && sqlite3_step(pStmt)==SQLITE_ROW ){
   349    347       i64 iId = sqlite3_column_int64(pStmt, 0);
   350         -    pIndex = sqlite3Fts5IndexFromCsrid(pTab->pGlobal, iId, &pConfig);
          348  +    pFts5 = sqlite3Fts5TableFromCsrid(pTab->pGlobal, iId);
   351    349     }
   352    350   
   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;
          351  +  if( rc==SQLITE_OK ){
          352  +    if( pFts5==0 ){
          353  +      rc = sqlite3_finalize(pStmt);
          354  +      pStmt = 0;
          355  +      if( rc==SQLITE_OK ){
          356  +        pVTab->zErrMsg = sqlite3_mprintf(
          357  +            "no such fts5 table: %s.%s", pTab->zFts5Db, pTab->zFts5Tbl
          358  +            );
          359  +        rc = SQLITE_ERROR;
          360  +      }
          361  +    }else{
          362  +      rc = sqlite3Fts5FlushToDisk(pFts5);
   361    363       }
   362    364     }
   363    365   
   364    366     if( rc==SQLITE_OK ){
   365         -    int nByte = pConfig->nCol * sizeof(i64) * 2 + sizeof(Fts5VocabCursor);
          367  +    int nByte = pFts5->pConfig->nCol * sizeof(i64)*2 + sizeof(Fts5VocabCursor);
   366    368       pCsr = (Fts5VocabCursor*)sqlite3Fts5MallocZero(&rc, nByte);
   367    369     }
   368    370   
   369    371     if( pCsr ){
   370         -    pCsr->pIndex = pIndex;
          372  +    pCsr->pFts5 = pFts5;
   371    373       pCsr->pStmt = pStmt;
   372         -    pCsr->pConfig = pConfig;
   373    374       pCsr->aCnt = (i64*)&pCsr[1];
   374         -    pCsr->aDoc = &pCsr->aCnt[pConfig->nCol];
          375  +    pCsr->aDoc = &pCsr->aCnt[pFts5->pConfig->nCol];
   375    376     }else{
   376    377       sqlite3_finalize(pStmt);
   377    378     }
   378    379   
   379    380     *ppCsr = (sqlite3_vtab_cursor*)pCsr;
   380    381     return rc;
   381    382   }
................................................................................
   383    384   static void fts5VocabResetCursor(Fts5VocabCursor *pCsr){
   384    385     pCsr->rowid = 0;
   385    386     sqlite3Fts5IterClose(pCsr->pIter);
   386    387     pCsr->pIter = 0;
   387    388     sqlite3_free(pCsr->zLeTerm);
   388    389     pCsr->nLeTerm = -1;
   389    390     pCsr->zLeTerm = 0;
          391  +  pCsr->bEof = 0;
   390    392   }
   391    393   
   392    394   /*
   393    395   ** Close the cursor.  For additional information see the documentation
   394    396   ** on the xClose method of the virtual table interface.
   395    397   */
   396    398   static int fts5VocabCloseMethod(sqlite3_vtab_cursor *pCursor){
................................................................................
   421    423   
   422    424       sqlite3Fts5BufferSet(&rc, &pCsr->term, nTerm, (const u8*)zTerm);
   423    425     }
   424    426     return rc;
   425    427   }
   426    428   
   427    429   static int fts5VocabInstanceNext(Fts5VocabCursor *pCsr){
   428         -  int eDetail = pCsr->pConfig->eDetail;
          430  +  int eDetail = pCsr->pFts5->pConfig->eDetail;
   429    431     int rc = SQLITE_OK;
   430    432     Fts5IndexIter *pIter = pCsr->pIter;
   431    433     i64 *pp = &pCsr->iInstPos;
   432    434     int *po = &pCsr->iInstOff;
   433    435     
   434    436     assert( sqlite3Fts5IterEof(pIter)==0 );
   435    437     assert( pCsr->bEof==0 );
................................................................................
   456    458   /*
   457    459   ** Advance the cursor to the next row in the table.
   458    460   */
   459    461   static int fts5VocabNextMethod(sqlite3_vtab_cursor *pCursor){
   460    462     Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor;
   461    463     Fts5VocabTable *pTab = (Fts5VocabTable*)pCursor->pVtab;
   462    464     int rc = SQLITE_OK;
   463         -  int nCol = pCsr->pConfig->nCol;
          465  +  int nCol = pCsr->pFts5->pConfig->nCol;
   464    466   
   465    467     pCsr->rowid++;
   466    468   
   467    469     if( pTab->eType==FTS5_VOCAB_INSTANCE ){
   468    470       return fts5VocabInstanceNext(pCsr);
   469    471     }
   470    472   
................................................................................
   494    496         sqlite3Fts5BufferSet(&rc, &pCsr->term, nTerm, (const u8*)zTerm);
   495    497         memset(pCsr->aCnt, 0, nCol * sizeof(i64));
   496    498         memset(pCsr->aDoc, 0, nCol * sizeof(i64));
   497    499         pCsr->iCol = 0;
   498    500   
   499    501         assert( pTab->eType==FTS5_VOCAB_COL || pTab->eType==FTS5_VOCAB_ROW );
   500    502         while( rc==SQLITE_OK ){
   501         -        int eDetail = pCsr->pConfig->eDetail;
          503  +        int eDetail = pCsr->pFts5->pConfig->eDetail;
   502    504           const u8 *pPos; int nPos;   /* Position list */
   503    505           i64 iPos = 0;               /* 64-bit position read from poslist */
   504    506           int iOff = 0;               /* Current offset within position list */
   505    507   
   506    508           pPos = pCsr->pIter->pData;
   507    509           nPos = pCsr->pIter->nData;
   508    510   
................................................................................
   565    567           }
   566    568         }
   567    569       }
   568    570     }
   569    571   
   570    572     if( rc==SQLITE_OK && pCsr->bEof==0 && pTab->eType==FTS5_VOCAB_COL ){
   571    573       while( pCsr->aDoc[pCsr->iCol]==0 ) pCsr->iCol++;
   572         -    assert( pCsr->iCol<pCsr->pConfig->nCol );
          574  +    assert( pCsr->iCol<pCsr->pFts5->pConfig->nCol );
   573    575     }
   574    576     return rc;
   575    577   }
   576    578   
   577    579   /*
   578    580   ** This is the xFilter implementation for the virtual table.
   579    581   */
................................................................................
   623    625         }else{
   624    626           memcpy(pCsr->zLeTerm, zCopy, pCsr->nLeTerm+1);
   625    627         }
   626    628       }
   627    629     }
   628    630   
   629    631     if( rc==SQLITE_OK ){
   630         -    rc = sqlite3Fts5IndexQuery(pCsr->pIndex, zTerm, nTerm, f, 0, &pCsr->pIter);
          632  +    Fts5Index *pIndex = pCsr->pFts5->pIndex;
          633  +    rc = sqlite3Fts5IndexQuery(pIndex, zTerm, nTerm, f, 0, &pCsr->pIter);
   631    634     }
   632    635     if( rc==SQLITE_OK && eType==FTS5_VOCAB_INSTANCE ){
   633    636       rc = fts5VocabInstanceNewTerm(pCsr);
   634    637     }
   635         -  if( rc==SQLITE_OK 
   636         -   && !pCsr->bEof 
   637         -   && (eType!=FTS5_VOCAB_INSTANCE || pCsr->pConfig->eDetail!=FTS5_DETAIL_NONE)
          638  +  if( rc==SQLITE_OK && !pCsr->bEof 
          639  +   && (eType!=FTS5_VOCAB_INSTANCE 
          640  +    || pCsr->pFts5->pConfig->eDetail!=FTS5_DETAIL_NONE)
   638    641     ){
   639    642       rc = fts5VocabNextMethod(pCursor);
   640    643     }
   641    644   
   642    645     return rc;
   643    646   }
   644    647   
................................................................................
   653    656   
   654    657   static int fts5VocabColumnMethod(
   655    658     sqlite3_vtab_cursor *pCursor,   /* Cursor to retrieve value from */
   656    659     sqlite3_context *pCtx,          /* Context for sqlite3_result_xxx() calls */
   657    660     int iCol                        /* Index of column to read value from */
   658    661   ){
   659    662     Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor;
   660         -  int eDetail = pCsr->pConfig->eDetail;
          663  +  int eDetail = pCsr->pFts5->pConfig->eDetail;
   661    664     int eType = ((Fts5VocabTable*)(pCursor->pVtab))->eType;
   662    665     i64 iVal = 0;
   663    666   
   664    667     if( iCol==0 ){
   665    668       sqlite3_result_text(
   666    669           pCtx, (const char*)pCsr->term.p, pCsr->term.n, SQLITE_TRANSIENT
   667    670       );
   668    671     }else if( eType==FTS5_VOCAB_COL ){
   669    672       assert( iCol==1 || iCol==2 || iCol==3 );
   670    673       if( iCol==1 ){
   671    674         if( eDetail!=FTS5_DETAIL_NONE ){
   672         -        const char *z = pCsr->pConfig->azCol[pCsr->iCol];
          675  +        const char *z = pCsr->pFts5->pConfig->azCol[pCsr->iCol];
   673    676           sqlite3_result_text(pCtx, z, -1, SQLITE_STATIC);
   674    677         }
   675    678       }else if( iCol==2 ){
   676    679         iVal = pCsr->aDoc[pCsr->iCol];
   677    680       }else{
   678    681         iVal = pCsr->aCnt[pCsr->iCol];
   679    682       }
................................................................................
   693    696         case 2: {
   694    697           int ii = -1;
   695    698           if( eDetail==FTS5_DETAIL_FULL ){
   696    699             ii = FTS5_POS2COLUMN(pCsr->iInstPos);
   697    700           }else if( eDetail==FTS5_DETAIL_COLUMNS ){
   698    701             ii = (int)pCsr->iInstPos;
   699    702           }
   700         -        if( ii>=0 && ii<pCsr->pConfig->nCol ){
   701         -          const char *z = pCsr->pConfig->azCol[ii];
          703  +        if( ii>=0 && ii<pCsr->pFts5->pConfig->nCol ){
          704  +          const char *z = pCsr->pFts5->pConfig->azCol[ii];
   702    705             sqlite3_result_text(pCtx, z, -1, SQLITE_STATIC);
   703    706           }
   704    707           break;
   705    708         }
   706    709         default: {
   707    710           assert( iCol==3 );
   708    711           if( eDetail==FTS5_DETAIL_FULL ){

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

   475    475     expr [lsearch $e2 SorterSort]<0
   476    476   } 1
   477    477   do_test 9.6 {
   478    478     set e2 [db eval { EXPLAIN SELECT * FROM rrr ORDER BY term DESC }]
   479    479     expr [lsearch $e2 SorterSort]<0
   480    480   } 0
   481    481   
          482  +#-------------------------------------------------------------------------
          483  +do_execsql_test 10.0 {
          484  +  CREATE VIRTUAL TABLE ft USING fts5(a, b, c);
          485  +  CREATE VIRTUAL TABLE t2 USING fts5vocab('ft','row');
          486  +  CREATE VIRTUAL TABLE t3 USING fts5vocab('ft','row');
          487  +}
          488  +
          489  +do_execsql_test 10.1 {
          490  +  BEGIN;
          491  +    INSERT INTO ft(b) VALUES('x y');
          492  +}
          493  +
          494  +do_execsql_test 10.2 {
          495  +  SELECT t2.term FROM t2;
          496  +} {x y}
          497  +
          498  +do_execsql_test 10.3 {
          499  +  SELECT t2.term, t3.term FROM t2, t3;
          500  +} {x x x y y x y y}
          501  +
          502  +do_execsql_test 10.4 {
          503  +  COMMIT;
          504  +}
          505  +
          506  +do_execsql_test 10.5 {
          507  +  BEGIN;
          508  +    INSERT INTO ft(a) VALUES('1 2 3');
          509  +    INSERT INTO ft(a) VALUES('4 5 6');
          510  +    INSERT INTO ft(a) VALUES('1 2 3');
          511  +    INSERT INTO ft(a) VALUES('4 5 6');
          512  +    INSERT INTO ft(a) VALUES('1 2 3');
          513  +    INSERT INTO ft(a) VALUES('4 5 6');
          514  +}
   482    515   
          516  +do_test 10.6 {
          517  +  set res [list]
          518  +  db eval { SELECT rowid FROM ft('4') } x {
          519  +    db eval { SELECT * FROM t2 }
          520  +    lappend res $x(rowid)
          521  +  }
          522  +  db eval COMMIT
          523  +  set res
          524  +} {3 5 7}
   483    525   
   484    526   finish_test
          527  +