/ Check-in [3515da85]
Login

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

Overview
Comment:Add simple full-table-scan and rowid lookup support to fts5.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | fts5
Files: files | file ages | folders
SHA1:3515da85d09220c464979467b476c611da4a6a7a
User & Date: dan 2014-06-24 16:59:06
Context
2014-06-25
20:28
Begin adding query support to fts5. check-in: 47a9f3cc user: dan tags: fts5
2014-06-24
16:59
Add simple full-table-scan and rowid lookup support to fts5. check-in: 3515da85 user: dan tags: fts5
2014-06-23
11:33
Add some code for an experimental fts5 module. Does not work yet. check-in: 1e0648dc user: dan tags: fts5
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to ext/fts5/fts5.c.

    12     12   **
    13     13   ** This is an SQLite module implementing full-text search.
    14     14   */
    15     15   
    16     16   #include "fts5Int.h"
    17     17   
    18     18   typedef struct Fts5Table Fts5Table;
           19  +typedef struct Fts5Cursor Fts5Cursor;
    19     20   
    20     21   struct Fts5Table {
    21     22     sqlite3_vtab base;              /* Base class used by SQLite core */
    22     23     Fts5Config *pConfig;            /* Virtual table configuration */
    23     24     Fts5Index *pIndex;              /* Full-text index */
    24     25     Fts5Storage *pStorage;          /* Document store */
    25     26   };
           27  +
           28  +struct Fts5Cursor {
           29  +  sqlite3_vtab_cursor base;       /* Base class used by SQLite core */
           30  +  int idxNum;                     /* idxNum passed to xFilter() */
           31  +  sqlite3_stmt *pStmt;            /* Statement used to read %_content */
           32  +  int bEof;                       /* True at EOF */
           33  +};
    26     34   
    27     35   /*
    28     36   ** Close a virtual table handle opened by fts5InitVtab(). If the bDestroy
    29     37   ** argument is non-zero, attempt delete the shadow tables from teh database
    30     38   */
    31     39   static int fts5FreeVtab(Fts5Table *pTab, int bDestroy){
    32     40     int rc = SQLITE_OK;
................................................................................
   140    148     int argc,                       /* Number of elements in argv array */
   141    149     const char * const *argv,       /* xCreate/xConnect argument array */
   142    150     sqlite3_vtab **ppVtab,          /* OUT: New sqlite3_vtab object */
   143    151     char **pzErr                    /* OUT: sqlite3_malloc'd error message */
   144    152   ){
   145    153     return fts5InitVtab(1, db, pAux, argc, argv, ppVtab, pzErr);
   146    154   }
          155  +
          156  +/*
          157  +** The three query plans xBestIndex may choose between.
          158  +*/
          159  +#define FTS5_PLAN_SCAN    1       /* No usable constraint */
          160  +#define FTS5_PLAN_MATCH   2       /* (<tbl> MATCH ?) */
          161  +#define FTS5_PLAN_ROWID   3       /* (rowid = ?) */
          162  +
          163  +#define FTS5_PLAN(idxNum) ((idxNum) & 0x7)
          164  +
          165  +#define FTS5_ORDER_DESC   8       /* ORDER BY rowid DESC */
          166  +#define FTS5_ORDER_ASC   16       /* ORDER BY rowid ASC */
          167  +
          168  +
          169  +static int fts5FindConstraint(sqlite3_index_info *pInfo, int eOp, int iCol){
          170  +  int i;
          171  +
          172  +  for(i=0; i<pInfo->nConstraint; i++){
          173  +    struct sqlite3_index_constraint *p = &pInfo->aConstraint[i];
          174  +    if( p->usable && p->iColumn==iCol && p->op==eOp ) return i;
          175  +  }
          176  +
          177  +  return -1;
          178  +}
   147    179   
   148    180   /* 
   149         -** Implementation of the xBestIndex method for FTS3 tables. There
          181  +** Implementation of the xBestIndex method for FTS5 tables. There
   150    182   ** are three possible strategies, in order of preference:
   151    183   **
   152         -**   1. Direct lookup by rowid or docid. 
   153         -**   2. Full-text search using a MATCH operator on a non-docid column.
   154         -**   3. Linear scan of %_content table.
          184  +**   1. Full-text search using a MATCH operator.
          185  +**   2. A by-rowid lookup.
          186  +**   3. A full-table scan.
   155    187   */
   156    188   static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
          189  +  Fts5Table *pTab = (Fts5Table*)pVTab;
          190  +  Fts5Config *pConfig = pTab->pConfig;
          191  +  int iCons;
          192  +  int ePlan = FTS5_PLAN_SCAN;
          193  +
          194  +  iCons = fts5FindConstraint(pInfo,SQLITE_INDEX_CONSTRAINT_MATCH,pConfig->nCol);
          195  +  if( iCons>=0 ){
          196  +    ePlan = FTS5_PLAN_MATCH;
          197  +    pInfo->estimatedCost = 1.0;
          198  +  }else{
          199  +    iCons = fts5FindConstraint(pInfo, SQLITE_INDEX_CONSTRAINT_EQ, -1);
          200  +    if( iCons>=0 ){
          201  +      ePlan = FTS5_PLAN_ROWID;
          202  +      pInfo->estimatedCost = 2.0;
          203  +    }
          204  +  }
          205  +
          206  +  if( iCons>=0 ){
          207  +    pInfo->aConstraintUsage[iCons].argvIndex = 1;
          208  +    pInfo->aConstraintUsage[iCons].omit = 1;
          209  +  }else{
          210  +    pInfo->estimatedCost = 10000000.0;
          211  +  }
          212  +
          213  +  if( pInfo->nOrderBy==1 && pInfo->aOrderBy[0].iColumn<0 ){
          214  +    pInfo->orderByConsumed = 1;
          215  +    ePlan |= pInfo->aOrderBy[0].desc ? FTS5_ORDER_DESC : FTS5_ORDER_ASC;
          216  +  }
          217  +   
          218  +  pInfo->idxNum = ePlan;
   157    219     return SQLITE_OK;
   158    220   }
   159    221   
   160    222   /*
   161    223   ** Implementation of xOpen method.
   162    224   */
   163    225   static int fts5OpenMethod(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCsr){
   164         -  return SQLITE_OK;
          226  +  Fts5Cursor *pCsr;
          227  +  int rc = SQLITE_OK;
          228  +  pCsr = (Fts5Cursor*)sqlite3_malloc(sizeof(Fts5Cursor));
          229  +  if( pCsr ){
          230  +    memset(pCsr, 0, sizeof(Fts5Cursor));
          231  +  }else{
          232  +    rc = SQLITE_NOMEM;
          233  +  }
          234  +  *ppCsr = (sqlite3_vtab_cursor*)pCsr;
          235  +  return rc;
          236  +}
          237  +
          238  +static int fts5StmtType(int idxNum){
          239  +  if( FTS5_PLAN(idxNum)==FTS5_PLAN_SCAN ){
          240  +    return (idxNum&FTS5_ORDER_ASC) ? FTS5_STMT_SCAN_ASC : FTS5_STMT_SCAN_DESC;
          241  +  }
          242  +  return FTS5_STMT_LOOKUP;
   165    243   }
   166    244   
   167    245   /*
   168    246   ** Close the cursor.  For additional information see the documentation
   169    247   ** on the xClose method of the virtual table interface.
   170    248   */
   171    249   static int fts5CloseMethod(sqlite3_vtab_cursor *pCursor){
          250  +  Fts5Table *pTab = (Fts5Table*)(pCursor->pVtab);
          251  +  Fts5Cursor *pCsr = (Fts5Cursor*)pCursor;
          252  +  if( pCsr->pStmt ){
          253  +    int eStmt = fts5StmtType(pCsr->idxNum);
          254  +    sqlite3Fts5StorageStmtRelease(pTab->pStorage, eStmt, pCsr->pStmt);
          255  +  }
          256  +  sqlite3_free(pCsr);
   172    257     return SQLITE_OK;
   173    258   }
   174    259   
   175    260   
   176    261   /*
   177    262   ** Advance the cursor to the next row in the table that matches the 
   178    263   ** search criteria.
   179    264   **
   180    265   ** Return SQLITE_OK if nothing goes wrong.  SQLITE_OK is returned
   181    266   ** even if we reach end-of-file.  The fts5EofMethod() will be called
   182    267   ** subsequently to determine whether or not an EOF was hit.
   183    268   */
   184    269   static int fts5NextMethod(sqlite3_vtab_cursor *pCursor){
   185         -  return SQLITE_OK;
          270  +  Fts5Cursor *pCsr = (Fts5Cursor*)pCursor;
          271  +  int ePlan = FTS5_PLAN(pCsr->idxNum);
          272  +  int rc = SQLITE_OK;
          273  +
          274  +  assert( ePlan!=FTS5_PLAN_MATCH );
          275  +  if( ePlan!=FTS5_PLAN_MATCH ){
          276  +    rc = sqlite3_step(pCsr->pStmt);
          277  +    if( rc!=SQLITE_ROW ){
          278  +      pCsr->bEof = 1;
          279  +      rc = sqlite3_reset(pCsr->pStmt);
          280  +    }else{
          281  +      rc = SQLITE_OK;
          282  +    }
          283  +  }
          284  +  
          285  +  return rc;
   186    286   }
   187    287   
   188    288   /*
   189    289   ** This is the xFilter interface for the virtual table.  See
   190    290   ** the virtual table xFilter method documentation for additional
   191    291   ** information.
   192    292   */
................................................................................
   193    293   static int fts5FilterMethod(
   194    294     sqlite3_vtab_cursor *pCursor,   /* The cursor used for this query */
   195    295     int idxNum,                     /* Strategy index */
   196    296     const char *idxStr,             /* Unused */
   197    297     int nVal,                       /* Number of elements in apVal */
   198    298     sqlite3_value **apVal           /* Arguments for the indexing scheme */
   199    299   ){
   200         -  return SQLITE_OK;
          300  +  Fts5Table *pTab = (Fts5Table*)(pCursor->pVtab);
          301  +  Fts5Cursor *pCsr = (Fts5Cursor*)pCursor;
          302  +  int rc = SQLITE_OK;
          303  +  int ePlan = FTS5_PLAN(idxNum);
          304  +  int eStmt = fts5StmtType(idxNum);
          305  +
          306  +  assert( ePlan!=FTS5_PLAN_MATCH );
          307  +  memset(&pCursor[1], 0, sizeof(Fts5Cursor) - sizeof(sqlite3_vtab_cursor));
          308  +  pCsr->idxNum = idxNum;
          309  +
          310  +  rc = sqlite3Fts5StorageStmt(pTab->pStorage, eStmt, &pCsr->pStmt);
          311  +  if( ePlan==FTS5_PLAN_ROWID ){
          312  +    sqlite3_bind_value(pCsr->pStmt, 1, apVal[0]);
          313  +  }
          314  +
          315  +  if( rc==SQLITE_OK ){
          316  +    rc = fts5NextMethod(pCursor);
          317  +  }
          318  +  return rc;
   201    319   }
   202    320   
   203    321   /* 
   204    322   ** This is the xEof method of the virtual table. SQLite calls this 
   205    323   ** routine to find out if it has reached the end of a result set.
   206    324   */
   207    325   static int fts5EofMethod(sqlite3_vtab_cursor *pCursor){
   208         -  return 1;
          326  +  Fts5Cursor *pCsr = (Fts5Cursor*)pCursor;
          327  +  return pCsr->bEof;
   209    328   }
   210    329   
   211    330   /* 
   212    331   ** This is the xRowid method. The SQLite core calls this routine to
   213    332   ** retrieve the rowid for the current row of the result set. fts5
   214    333   ** exposes %_content.docid as the rowid for the virtual table. The
   215    334   ** rowid should be written to *pRowid.
   216    335   */
   217    336   static int fts5RowidMethod(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
          337  +  Fts5Cursor *pCsr = (Fts5Cursor*)pCursor;
          338  +  int ePlan = FTS5_PLAN(pCsr->idxNum);
          339  +  
          340  +  assert( pCsr->bEof==0 );
          341  +  assert( ePlan!=FTS5_PLAN_MATCH );
          342  +
          343  +  if( ePlan!=FTS5_PLAN_MATCH ){
          344  +    *pRowid = sqlite3_column_int64(pCsr->pStmt, 0);
          345  +  }
          346  +
   218    347     return SQLITE_OK;
   219    348   }
   220    349   
   221    350   /* 
   222    351   ** This is the xColumn method, called by SQLite to request a value from
   223    352   ** the row that the supplied cursor currently points to.
   224    353   */
   225    354   static int fts5ColumnMethod(
   226    355     sqlite3_vtab_cursor *pCursor,   /* Cursor to retrieve value from */
   227    356     sqlite3_context *pCtx,          /* Context for sqlite3_result_xxx() calls */
   228    357     int iCol                        /* Index of column to read value from */
   229    358   ){
          359  +  Fts5Cursor *pCsr = (Fts5Cursor*)pCursor;
          360  +  int ePlan = FTS5_PLAN(pCsr->idxNum);
          361  +  
          362  +  assert( pCsr->bEof==0 );
          363  +  assert( ePlan!=FTS5_PLAN_MATCH );
          364  +  if( ePlan!=FTS5_PLAN_MATCH ){
          365  +    sqlite3_result_value(pCtx, sqlite3_column_value(pCsr->pStmt, iCol+1));
          366  +  }
   230    367     return SQLITE_OK;
   231    368   }
   232    369   
   233    370   /*
   234    371   ** This function is called to handle an FTS INSERT command. In other words,
   235    372   ** an INSERT statement of the form:
   236    373   **
................................................................................
   237    374   **     INSERT INTO fts(fts) VALUES($pVal)
   238    375   **
   239    376   ** Argument pVal is the value assigned to column "fts" by the INSERT 
   240    377   ** statement. This function returns SQLITE_OK if successful, or an SQLite
   241    378   ** error code if an error occurs.
   242    379   */
   243    380   static int fts5SpecialCommand(Fts5Table *pTab, sqlite3_value *pVal){
   244         -  const char *z = sqlite3_value_text(pVal);
          381  +  const char *z = (const char*)sqlite3_value_text(pVal);
   245    382     int n = sqlite3_value_bytes(pVal);
   246    383     int rc = SQLITE_ERROR;
   247    384   
   248    385     if( 0==sqlite3_stricmp("integrity-check", z) ){
   249    386       rc = sqlite3Fts5StorageIntegrity(pTab->pStorage);
   250    387     }else
   251    388   

Changes to ext/fts5/fts5Int.h.

   221    221   int sqlite3Fts5DropTable(Fts5Config*, const char *zPost);
   222    222   int sqlite3Fts5CreateTable(Fts5Config*, const char*, const char*, char **pzErr);
   223    223   
   224    224   int sqlite3Fts5StorageDelete(Fts5Storage *p, i64);
   225    225   int sqlite3Fts5StorageInsert(Fts5Storage *p, sqlite3_value **apVal, int, i64*);
   226    226   
   227    227   int sqlite3Fts5StorageIntegrity(Fts5Storage *p);
          228  +
          229  +#define FTS5_STMT_SCAN_ASC  0     /* SELECT rowid, * FROM ... ORDER BY 1 ASC */
          230  +#define FTS5_STMT_SCAN_DESC 1     /* SELECT rowid, * FROM ... ORDER BY 1 DESC */
          231  +#define FTS5_STMT_LOOKUP    2     /* SELECT rowid, * FROM ... WHERE rowid=? */
          232  +
          233  +int sqlite3Fts5StorageStmt(Fts5Storage *p, int eStmt, sqlite3_stmt **);
          234  +void sqlite3Fts5StorageStmtRelease(Fts5Storage *p, int eStmt, sqlite3_stmt*);
          235  +
          236  +
   228    237   
   229    238   /*
   230    239   ** End of interface to code in fts5_storage.c.
   231    240   **************************************************************************/
   232    241   
   233    242   
   234    243   /**************************************************************************

Changes to ext/fts5/fts5_storage.c.

    14     14   
    15     15   #include "fts5Int.h"
    16     16   
    17     17   struct Fts5Storage {
    18     18     Fts5Config *pConfig;
    19     19     Fts5Index *pIndex;
    20     20   
    21         -  sqlite3_stmt *aStmt[7];
           21  +  sqlite3_stmt *aStmt[8];
    22     22   };
    23     23   
    24         -#define FTS5_STMT_INSERT_CONTENT  0
    25         -#define FTS5_STMT_REPLACE_CONTENT 1
    26     24   
    27         -#define FTS5_STMT_DELETE_CONTENT  2
    28         -#define FTS5_STMT_INSERT_DOCSIZE  3
    29         -#define FTS5_STMT_DELETE_DOCSIZE  4
           25  +#if FTS5_STMT_SCAN_ASC!=0 
           26  +# error "FTS5_STMT_SCAN_ASC mismatch" 
           27  +#endif
           28  +#if FTS5_STMT_SCAN_DESC!=1 
           29  +# error "FTS5_STMT_SCAN_DESC mismatch" 
           30  +#endif
           31  +#if FTS5_STMT_LOOKUP!=2
           32  +# error "FTS5_STMT_LOOKUP mismatch" 
           33  +#endif
    30     34   
    31         -#define FTS5_STMT_SCAN_CONTENT    5
    32         -#define FTS5_STMT_SEEK_CONTENT    6
           35  +#define FTS5_STMT_INSERT_CONTENT  3
           36  +#define FTS5_STMT_REPLACE_CONTENT 4
           37  +
           38  +#define FTS5_STMT_DELETE_CONTENT  5
           39  +#define FTS5_STMT_INSERT_DOCSIZE  6
           40  +#define FTS5_STMT_DELETE_DOCSIZE  7
    33     41   
    34     42   /*
    35     43   ** Prepare the two insert statements - Fts5Storage.pInsertContent and
    36     44   ** Fts5Storage.pInsertDocsize - if they have not already been prepared.
    37     45   ** Return SQLITE_OK if successful, or an SQLite error code if an error
    38     46   ** occurs.
    39     47   */
................................................................................
    43     51     sqlite3_stmt **ppStmt           /* OUT: Prepared statement handle */
    44     52   ){
    45     53     int rc = SQLITE_OK;
    46     54   
    47     55     assert( eStmt>=0 && eStmt<ArraySize(p->aStmt) );
    48     56     if( p->aStmt[eStmt]==0 ){
    49     57       const char *azStmt[] = {
    50         -      "INSERT INTO %Q.'%q_content' VALUES(%s)",       /* INSERT_CONTENT  */
    51         -      "REPLACE INTO %Q.'%q_content' VALUES(%s)",      /* REPLACE_CONTENT */
    52         -      "DELETE FROM %Q.'%q_content' WHERE id=?",       /* DELETE_CONTENT  */
    53         -      "INSERT INTO %Q.'%q_docsize' VALUES(?,?)",      /* INSERT_DOCSIZE  */
    54         -      "DELETE FROM %Q.'%q_docsize' WHERE id=?",       /* DELETE_DOCSIZE  */
    55         -      "SELECT * FROM %Q.'%q_content'",                /* SCAN_CONTENT  */
    56         -      "SELECT * FROM %Q.'%q_content' WHERE rowid=?",  /* SEEK_CONTENT  */
           58  +      "SELECT * FROM %Q.'%q_content' ORDER BY id ASC",  /* SCAN_ASC */
           59  +      "SELECT * FROM %Q.'%q_content' ORDER BY id DESC", /* SCAN_DESC */
           60  +      "SELECT * FROM %Q.'%q_content' WHERE rowid=?",    /* LOOKUP  */
           61  +
           62  +      "INSERT INTO %Q.'%q_content' VALUES(%s)",         /* INSERT_CONTENT  */
           63  +      "REPLACE INTO %Q.'%q_content' VALUES(%s)",        /* REPLACE_CONTENT */
           64  +      "DELETE FROM %Q.'%q_content' WHERE id=?",         /* DELETE_CONTENT  */
           65  +      "INSERT INTO %Q.'%q_docsize' VALUES(?,?)",        /* INSERT_DOCSIZE  */
           66  +      "DELETE FROM %Q.'%q_docsize' WHERE id=?",         /* DELETE_DOCSIZE  */
    57     67       };
    58     68       Fts5Config *pConfig = p->pConfig;
    59     69       char *zSql = 0;
    60     70   
    61     71       if( eStmt==FTS5_STMT_INSERT_CONTENT || eStmt==FTS5_STMT_REPLACE_CONTENT ){
    62     72         int nCol = pConfig->nCol + 1;
    63     73         char *zBind;
................................................................................
   249    259   ** remove the %_content row at this time though.
   250    260   */
   251    261   static int fts5StorageDeleteFromIndex(Fts5Storage *p, i64 iDel){
   252    262     Fts5Config *pConfig = p->pConfig;
   253    263     sqlite3_stmt *pSeek;            /* SELECT to read row iDel from %_data */
   254    264     int rc;                         /* Return code */
   255    265   
   256         -  rc = fts5StorageGetStmt(p, FTS5_STMT_SEEK_CONTENT, &pSeek);
          266  +  rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP, &pSeek);
   257    267     if( rc==SQLITE_OK ){
   258    268       int rc2;
   259    269       sqlite3_bind_int64(pSeek, 1, iDel);
   260    270       if( sqlite3_step(pSeek)==SQLITE_ROW ){
   261    271         int iCol;
   262    272         Fts5InsertCtx ctx;
   263    273         ctx.pStorage = p;
................................................................................
   373    383     sqlite3_stmt *pScan;
   374    384   
   375    385     memset(&ctx, 0, sizeof(Fts5IntegrityCtx));
   376    386     ctx.pConfig = p->pConfig;
   377    387   
   378    388     /* Generate the expected index checksum based on the contents of the
   379    389     ** %_content table. This block stores the checksum in ctx.cksum. */
   380         -  rc = fts5StorageGetStmt(p, FTS5_STMT_SCAN_CONTENT, &pScan);
          390  +  rc = fts5StorageGetStmt(p, FTS5_STMT_SCAN_ASC, &pScan);
   381    391     if( rc==SQLITE_OK ){
   382    392       int rc2;
   383    393       while( SQLITE_ROW==sqlite3_step(pScan) ){
   384    394         int i;
   385    395         ctx.iRowid = sqlite3_column_int64(pScan, 0);
   386    396         for(i=0; rc==SQLITE_OK && i<pConfig->nCol; i++){
   387    397           ctx.iCol = i;
................................................................................
   403    413     ** inspecting the index itself.  */
   404    414     if( rc==SQLITE_OK ){
   405    415       rc = sqlite3Fts5IndexIntegrityCheck(p->pIndex, ctx.cksum);
   406    416     }
   407    417   
   408    418     return rc;
   409    419   }
          420  +
          421  +/*
          422  +** Obtain an SQLite statement handle that may be used to read data from the
          423  +** %_content table.
          424  +*/
          425  +int sqlite3Fts5StorageStmt(Fts5Storage *p, int eStmt, sqlite3_stmt **pp){
          426  +  int rc;
          427  +  assert( eStmt==FTS5_STMT_SCAN_ASC 
          428  +       || eStmt==FTS5_STMT_SCAN_DESC
          429  +       || eStmt==FTS5_STMT_LOOKUP
          430  +  );
          431  +  rc = fts5StorageGetStmt(p, eStmt, pp);
          432  +  if( rc==SQLITE_OK ){
          433  +    assert( p->aStmt[eStmt]==*pp );
          434  +    p->aStmt[eStmt] = 0;
          435  +  }
          436  +  return rc;
          437  +}
          438  +
          439  +/*
          440  +** Release an SQLite statement handle obtained via an earlier call to
          441  +** sqlite3Fts5StorageStmt(). The eStmt parameter passed to this function
          442  +** must match that passed to the sqlite3Fts5StorageStmt() call.
          443  +*/
          444  +void sqlite3Fts5StorageStmtRelease(
          445  +  Fts5Storage *p, 
          446  +  int eStmt, 
          447  +  sqlite3_stmt *pStmt
          448  +){
          449  +  assert( eStmt==FTS5_STMT_SCAN_ASC
          450  +       || eStmt==FTS5_STMT_SCAN_DESC
          451  +       || eStmt==FTS5_STMT_LOOKUP
          452  +  );
          453  +  if( p->aStmt[eStmt]==0 ){
          454  +    sqlite3_reset(pStmt);
          455  +    p->aStmt[eStmt] = pStmt;
          456  +  }else{
          457  +    sqlite3_finalize(pStmt);
          458  +  }
          459  +}
   410    460   
   411    461   

Changes to test/fts5aa.test.

   193    193   }
   194    194   
   195    195   do_execsql_test 8.1 {
   196    196     INSERT INTO t1 VALUES('the quick brown fox');
   197    197     INSERT INTO t1(t1) VALUES('integrity-check');
   198    198   }
   199    199   
   200         -
   201         -#finish_test
   202         -
   203    200   
   204    201   #-------------------------------------------------------------------------
   205    202   #
   206    203   reset_db
   207    204   
   208    205   expr srand(0)
   209    206   

Added test/fts5ab.test.

            1  +# 2014 June 17
            2  +#
            3  +# The author disclaims copyright to this source code.  In place of
            4  +# a legal notice, here is a blessing:
            5  +#
            6  +#    May you do good and not evil.
            7  +#    May you find forgiveness for yourself and forgive others.
            8  +#    May you share freely, never taking more than you give.
            9  +#
           10  +#*************************************************************************
           11  +# This file implements regression tests for SQLite library.  The
           12  +# focus of this script is testing the FTS5 module.
           13  +#
           14  +#
           15  +
           16  +set testdir [file dirname $argv0]
           17  +source $testdir/tester.tcl
           18  +set testprefix fts5ab
           19  +
           20  +# If SQLITE_ENABLE_FTS3 is defined, omit this file.
           21  +ifcapable !fts3 {
           22  +  finish_test
           23  +  return
           24  +}
           25  +
           26  +do_execsql_test 1.0 {
           27  +  CREATE VIRTUAL TABLE t1 USING fts5(a, b);
           28  +  INSERT INTO t1 VALUES('hello', 'world');
           29  +  INSERT INTO t1 VALUES('one two', 'three four');
           30  +  INSERT INTO t1(rowid, a, b) VALUES(45, 'forty', 'five');
           31  +}
           32  +
           33  +do_execsql_test 1.1 {
           34  +  SELECT * FROM t1;
           35  +} { forty five {one two} {three four} hello world }
           36  +
           37  +do_execsql_test 1.2 {
           38  +  SELECT rowid FROM t1;
           39  +} {45 2 1}
           40  +
           41  +do_execsql_test 1.3 {
           42  +  SELECT rowid FROM t1 ORDER BY rowid ASC;
           43  +} {1 2 45}
           44  +
           45  +do_execsql_test 1.4 {
           46  +  SELECT * FROM t1 WHERE rowid=2;
           47  +} {{one two} {three four}}
           48  +
           49  +do_execsql_test 1.5 {
           50  +  SELECT * FROM t1 WHERE rowid=2.01;
           51  +} {}
           52  +
           53  +do_execsql_test 1.6 {
           54  +  SELECT * FROM t1 WHERE rowid=1.99;
           55  +} {}
           56  +
           57  +finish_test