/ Check-in [c1f07a3a]
Login

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

Overview
Comment:Improve fts5 tests.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | fts5
Files: files | file ages | folders
SHA1: c1f07a3aa98eac87e2747527d15e5e5562221ceb
User & Date: dan 2015-04-29 20:54:08
Context
2015-05-01
12:14
Improve test coverage of fts5.c. check-in: add4f468 user: dan tags: fts5
2015-04-29
20:54
Improve fts5 tests. check-in: c1f07a3a user: dan tags: fts5
2015-04-28
20:24
Fix an fts5 bug in handling writes while there are active cursors. check-in: 07f70955 user: dan tags: fts5
Changes
Hide Diffs Side-by-Side Diffs Show Whitespace Changes Patch

Changes to ext/fts5/fts5.c.

   525    525   }
   526    526   
   527    527   /*
   528    528   ** Close the cursor.  For additional information see the documentation
   529    529   ** on the xClose method of the virtual table interface.
   530    530   */
   531    531   static int fts5CloseMethod(sqlite3_vtab_cursor *pCursor){
          532  +  if( pCursor ){
   532    533     Fts5Table *pTab = (Fts5Table*)(pCursor->pVtab);
   533    534     Fts5Cursor *pCsr = (Fts5Cursor*)pCursor;
   534    535     Fts5Cursor **pp;
   535    536     Fts5Auxdata *pData;
   536    537     Fts5Auxdata *pNext;
   537    538   
   538    539     fts5CsrNewrow(pCsr);
................................................................................
   565    566   
   566    567     sqlite3_free(pCsr->zSpecial);
   567    568     if( CsrFlagTest(pCsr, FTS5CSR_FREE_ZRANK) ){
   568    569       sqlite3_free(pCsr->zRank);
   569    570       sqlite3_free(pCsr->zRankArgs);
   570    571     }
   571    572     sqlite3_free(pCsr);
          573  +  }
   572    574     return SQLITE_OK;
   573    575   }
   574    576   
   575    577   static int fts5SorterNext(Fts5Cursor *pCsr){
   576    578     Fts5Sorter *pSorter = pCsr->pSorter;
   577    579     int rc;
   578    580   
................................................................................
   889    891   ){
   890    892     int rc = SQLITE_OK;
   891    893     if( pRank ){
   892    894       const char *z = (const char*)sqlite3_value_text(pRank);
   893    895       char *zRank = 0;
   894    896       char *zRankArgs = 0;
   895    897   
          898  +    if( z==0 ){
          899  +      if( sqlite3_value_type(pRank)==SQLITE_NULL ) rc = SQLITE_ERROR;
          900  +    }else{
   896    901       rc = sqlite3Fts5ConfigParseRank(z, &zRank, &zRankArgs);
          902  +    }
   897    903       if( rc==SQLITE_OK ){
   898    904         pCsr->zRank = zRank;
   899    905         pCsr->zRankArgs = zRankArgs;
   900    906         CsrFlagSet(pCsr, FTS5CSR_FREE_ZRANK);
   901    907       }else if( rc==SQLITE_ERROR ){
   902    908         pCsr->base.pVtab->zErrMsg = sqlite3_mprintf(
   903    909             "parse error in rank function: %s", z
................................................................................
  1203   1209     ** Update and insert operations pass:
  1204   1210     **
  1205   1211     **   1. The "old" rowid, or NULL.
  1206   1212     **   2. The "new" rowid.
  1207   1213     **   3. Values for each of the nCol matchable columns.
  1208   1214     **   4. Values for the two hidden columns (<tablename> and "rank").
  1209   1215     */
  1210         -  assert( nArg==1 || nArg==(2 + pConfig->nCol + 2) );
  1211   1216   
  1212   1217     eType0 = sqlite3_value_type(apVal[0]);
  1213   1218     eConflict = sqlite3_vtab_on_conflict(pConfig->db);
  1214   1219   
  1215   1220     assert( eType0==SQLITE_INTEGER || eType0==SQLITE_NULL );
  1216   1221     assert( pVtab->zErrMsg==0 );
         1222  +  assert( (nArg==1 && eType0==SQLITE_INTEGER) || nArg==(2+pConfig->nCol+2) );
  1217   1223   
  1218   1224     fts5TripCursors(pTab);
  1219         -  if( rc==SQLITE_OK && eType0==SQLITE_INTEGER ){
         1225  +  if( eType0==SQLITE_INTEGER ){
  1220   1226       if( fts5IsContentless(pTab) ){
  1221   1227         pTab->base.zErrMsg = sqlite3_mprintf(
  1222   1228             "cannot %s contentless fts5 table: %s", 
  1223   1229             (nArg>1 ? "UPDATE" : "DELETE from"), pConfig->zName
  1224   1230         );
  1225   1231         rc = SQLITE_ERROR;
  1226   1232       }else{
  1227   1233         i64 iDel = sqlite3_value_int64(apVal[0]);  /* Rowid to delete */
  1228   1234         rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel);
  1229   1235       }
  1230         -  }else if( nArg>1 ){
         1236  +  }else{
         1237  +    assert( nArg>1 );
  1231   1238       sqlite3_value *pCmd = apVal[2 + pConfig->nCol];
  1232   1239       if( SQLITE_NULL!=sqlite3_value_type(pCmd) ){
  1233   1240         const char *z = (const char*)sqlite3_value_text(pCmd);
  1234   1241         if( pConfig->eContent!=FTS5_CONTENT_NORMAL 
  1235   1242          && 0==sqlite3_stricmp("delete", z) 
  1236   1243         ){
  1237   1244           return fts5SpecialDelete(pTab, apVal, pRowid);
................................................................................
  1467   1474     Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
  1468   1475     Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
  1469   1476     int rc = SQLITE_OK;
  1470   1477   
  1471   1478     if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_DOCSIZE) ){
  1472   1479       i64 iRowid = fts5CursorRowid(pCsr);
  1473   1480       rc = sqlite3Fts5StorageDocsize(pTab->pStorage, iRowid, pCsr->aColumnSize);
         1481  +    CsrFlagClear(pCsr, FTS5CSR_REQUIRE_DOCSIZE);
  1474   1482     }
  1475   1483     if( iCol<0 ){
  1476   1484       int i;
  1477   1485       *pnToken = 0;
  1478   1486       for(i=0; i<pTab->pConfig->nCol; i++){
  1479   1487         *pnToken += pCsr->aColumnSize[i];
  1480   1488       }
................................................................................
  1874   1882       }
  1875   1883     }else{
  1876   1884       rc = SQLITE_NOMEM;
  1877   1885     }
  1878   1886   
  1879   1887     return rc;
  1880   1888   }
         1889  +
         1890  +static Fts5TokenizerModule *fts5LocateTokenizer(
         1891  +  Fts5Global *pGlobal, 
         1892  +  const char *zName
         1893  +){
         1894  +  Fts5TokenizerModule *pMod = 0;
         1895  +
         1896  +  if( zName==0 ){
         1897  +    pMod = pGlobal->pDfltTok;
         1898  +  }else{
         1899  +    for(pMod=pGlobal->pTok; pMod; pMod=pMod->pNext){
         1900  +      if( sqlite3_stricmp(zName, pMod->zName)==0 ) break;
         1901  +    }
         1902  +  }
         1903  +
         1904  +  return pMod;
         1905  +}
  1881   1906   
  1882   1907   /*
  1883   1908   ** Find a tokenizer. This is the implementation of the 
  1884   1909   ** fts5_api.xFindTokenizer() method.
  1885   1910   */
  1886   1911   static int fts5FindTokenizer(
  1887   1912     fts5_api *pApi,                 /* Global context (one per db handle) */
  1888   1913     const char *zName,              /* Name of new function */
  1889   1914     void **ppUserData,
  1890   1915     fts5_tokenizer *pTokenizer      /* Populate this object */
  1891   1916   ){
  1892         -  Fts5Global *pGlobal = (Fts5Global*)pApi;
  1893   1917     int rc = SQLITE_OK;
  1894         -  Fts5TokenizerModule *pTok;
         1918  +  Fts5TokenizerModule *pMod;
  1895   1919   
  1896         -  if( zName==0 ){
  1897         -    pTok = pGlobal->pDfltTok;
  1898         -  }else{
  1899         -    for(pTok=pGlobal->pTok; pTok; pTok=pTok->pNext){
  1900         -      if( sqlite3_stricmp(zName, pTok->zName)==0 ) break;
  1901         -    }
  1902         -  }
  1903         -
  1904         -  if( pTok ){
  1905         -    *pTokenizer = pTok->x;
  1906         -    *ppUserData = pTok->pUserData;
         1920  +  pMod = fts5LocateTokenizer((Fts5Global*)pApi, zName);
         1921  +  if( pMod ){
         1922  +    *pTokenizer = pMod->x;
         1923  +    *ppUserData = pMod->pUserData;
  1907   1924     }else{
  1908   1925       memset(pTokenizer, 0, sizeof(fts5_tokenizer));
  1909   1926       rc = SQLITE_ERROR;
  1910   1927     }
  1911   1928   
  1912   1929     return rc;
  1913   1930   }
  1914   1931   
  1915   1932   int sqlite3Fts5GetTokenizer(
  1916   1933     Fts5Global *pGlobal, 
  1917   1934     const char **azArg,
  1918   1935     int nArg,
  1919   1936     Fts5Tokenizer **ppTok,
  1920         -  fts5_tokenizer **ppTokApi
         1937  +  fts5_tokenizer **ppTokApi,
         1938  +  char **pzErr
  1921   1939   ){
  1922         -  Fts5TokenizerModule *pMod = 0;
         1940  +  Fts5TokenizerModule *pMod;
  1923   1941     int rc = SQLITE_OK;
  1924   1942   
  1925         -  if( nArg==0 ){
  1926         -    pMod = pGlobal->pDfltTok;
  1927         -  }else{
  1928         -    for(pMod=pGlobal->pTok; pMod; pMod=pMod->pNext){
  1929         -      if( sqlite3_stricmp(azArg[0], pMod->zName)==0 ) break;
  1930         -    }
  1931         -  }
  1932         -
         1943  +  pMod = fts5LocateTokenizer(pGlobal, nArg==0 ? 0 : azArg[0]);
  1933   1944     if( pMod==0 ){
         1945  +    assert( nArg>0 );
  1934   1946       rc = SQLITE_ERROR;
         1947  +    *pzErr = sqlite3_mprintf("no such tokenizer: %s", azArg[0]);
  1935   1948     }else{
  1936   1949       rc = pMod->x.xCreate(pMod->pUserData, &azArg[1], (nArg?nArg-1:0), ppTok);
  1937   1950       *ppTokApi = &pMod->x;
         1951  +    if( rc!=SQLITE_OK && pzErr ){
         1952  +      *pzErr = sqlite3_mprintf("error in tokenizer constructor");
         1953  +    }
  1938   1954     }
  1939   1955   
  1940   1956     if( rc!=SQLITE_OK ){
  1941   1957       *ppTokApi = 0;
  1942   1958       *ppTok = 0;
  1943   1959     }
  1944   1960   

Changes to ext/fts5/fts5Int.h.

    57     57   typedef struct Fts5Global Fts5Global;
    58     58   
    59     59   int sqlite3Fts5GetTokenizer(
    60     60     Fts5Global*, 
    61     61     const char **azArg,
    62     62     int nArg,
    63     63     Fts5Tokenizer**,
    64         -  fts5_tokenizer**
           64  +  fts5_tokenizer**,
           65  +  char **pzErr
    65     66   );
    66     67   
    67     68   /*
    68     69   ** End of interface to code in fts5.c.
    69     70   **************************************************************************/
    70     71   
    71     72   /**************************************************************************

Changes to ext/fts5/fts5_aux.c.

   226    226       if( rc==SQLITE_OK ){
   227    227         rc = pApi->xTokenize(pFts, ctx.zIn, ctx.nIn, (void*)&ctx,fts5HighlightCb);
   228    228       }
   229    229       fts5HighlightAppend(&rc, &ctx, &ctx.zIn[ctx.iOff], ctx.nIn - ctx.iOff);
   230    230   
   231    231       if( rc==SQLITE_OK ){
   232    232         sqlite3_result_text(pCtx, (const char*)ctx.zOut, -1, SQLITE_TRANSIENT);
   233         -    }else{
   234         -      sqlite3_result_error_code(pCtx, rc);
   235    233       }
   236    234       sqlite3_free(ctx.zOut);
   237    235     }
          236  +  if( rc!=SQLITE_OK ){
          237  +    sqlite3_result_error_code(pCtx, rc);
          238  +  }
   238    239   }
   239    240   /*
   240    241   ** End of highlight() implementation.
   241    242   **************************************************************************/
   242    243   
   243    244   /*
   244    245   ** Implementation of snippet() function.

Changes to ext/fts5/fts5_config.c.

   326    326             }
   327    327           }
   328    328           if( p==0 ){
   329    329             *pzErr = sqlite3_mprintf("parse error in tokenize directive");
   330    330             rc = SQLITE_ERROR;
   331    331           }else{
   332    332             rc = sqlite3Fts5GetTokenizer(pGlobal, 
   333         -              (const char**)azArg, nArg, &pConfig->pTok, &pConfig->pTokApi
          333  +              (const char**)azArg, nArg, &pConfig->pTok, &pConfig->pTokApi,
          334  +              pzErr
   334    335             );
   335         -          if( rc!=SQLITE_OK ){
   336         -            *pzErr = sqlite3_mprintf("error in tokenizer constructor");
   337         -          }
   338    336           }
   339    337         }
   340    338       }
   341    339   
   342    340       sqlite3_free(azArg);
   343    341       sqlite3_free(pDel);
   344    342       return rc;
................................................................................
   383    381   ** Allocate an instance of the default tokenizer ("simple") at 
   384    382   ** Fts5Config.pTokenizer. Return SQLITE_OK if successful, or an SQLite error
   385    383   ** code if an error occurs.
   386    384   */
   387    385   static int fts5ConfigDefaultTokenizer(Fts5Global *pGlobal, Fts5Config *pConfig){
   388    386     assert( pConfig->pTok==0 && pConfig->pTokApi==0 );
   389    387     return sqlite3Fts5GetTokenizer(
   390         -      pGlobal, 0, 0, &pConfig->pTok, &pConfig->pTokApi
          388  +      pGlobal, 0, 0, &pConfig->pTok, &pConfig->pTokApi, 0
   391    389     );
   392    390   }
   393    391   
   394    392   /*
   395    393   ** Gobble up the first bareword or quoted word from the input buffer zIn.
   396    394   ** Return a pointer to the character immediately following the last in
   397    395   ** the gobbled word if successful, or a NULL pointer otherwise (failed
................................................................................
   559    557   
   560    558       if( rc==SQLITE_OK ){
   561    559         if( z==0 ){
   562    560           *pzErr = sqlite3_mprintf("parse error in \"%s\"", zOrig);
   563    561           rc = SQLITE_ERROR;
   564    562         }else{
   565    563           if( bOption ){
   566         -          rc = fts5ConfigParseSpecial(pGlobal, pRet, zOne, zTwo, pzErr);
          564  +          rc = fts5ConfigParseSpecial(pGlobal, pRet, zOne, zTwo?zTwo:"", pzErr);
   567    565           }else{
   568    566             rc = fts5ConfigParseColumn(pRet, zOne, zTwo, pzErr);
   569    567             zOne = 0;
   570    568           }
   571    569         }
   572    570       }
   573    571   

Changes to ext/fts5/fts5_expr.c.

   276    276     Fts5Expr *pNew;
   277    277     Fts5ExprPhrase **apPhrase;
   278    278     Fts5ExprNode *pNode;
   279    279     Fts5ExprNearset *pNear;
   280    280     Fts5ExprPhrase *pCopy;
   281    281   
   282    282     pOrig = pExpr->apExprPhrase[iPhrase];
          283  +  pCopy = (Fts5ExprPhrase*)fts5ExprMalloc(&rc, 
          284  +      sizeof(Fts5ExprPhrase) + sizeof(Fts5ExprTerm) * pOrig->nTerm
          285  +  );
   283    286     pNew = (Fts5Expr*)fts5ExprMalloc(&rc, sizeof(Fts5Expr));
   284    287     apPhrase = (Fts5ExprPhrase**)fts5ExprMalloc(&rc, sizeof(Fts5ExprPhrase*));
   285    288     pNode = (Fts5ExprNode*)fts5ExprMalloc(&rc, sizeof(Fts5ExprNode));
   286    289     pNear = (Fts5ExprNearset*)fts5ExprMalloc(&rc, 
   287    290         sizeof(Fts5ExprNearset) + sizeof(Fts5ExprPhrase*)
   288    291     );
   289         -  pCopy = (Fts5ExprPhrase*)fts5ExprMalloc(&rc, 
   290         -      sizeof(Fts5ExprPhrase) + sizeof(Fts5ExprTerm) * pOrig->nTerm
   291         -  );
   292    292   
   293    293     for(i=0; rc==SQLITE_OK && i<pOrig->nTerm; i++){
   294    294       pCopy->aTerm[i].zTerm = fts5ExprStrdup(&rc, pOrig->aTerm[i].zTerm);
   295    295       pCopy->aTerm[i].bPrefix = pOrig->aTerm[i].bPrefix;
   296    296     }
   297    297   
   298    298     if( rc==SQLITE_OK ){

Changes to ext/fts5/fts5_tcl.c.

    32     32   
    33     33   /*************************************************************************
    34     34   ** This is a copy of the first part of the SqliteDb structure in 
    35     35   ** tclsqlite.c.  We need it here so that the get_sqlite_pointer routine
    36     36   ** can extract the sqlite3* pointer from an existing Tcl SQLite
    37     37   ** connection.
    38     38   */
           39  +
           40  +extern const char *sqlite3ErrName(int);
           41  +
    39     42   struct SqliteDb {
    40     43     sqlite3 *db;
    41     44   };
    42     45   
    43     46   /*
    44     47   ** Decode a pointer to an sqlite3 object.
    45     48   */
................................................................................
   386    389       default: 
   387    390         assert( 0 );
   388    391         break;
   389    392     }
   390    393   #undef CASE
   391    394   
   392    395     if( rc!=SQLITE_OK ){
   393         -    Tcl_AppendResult(interp, "error in api call", 0);
          396  +    Tcl_SetResult(interp, (char*)sqlite3ErrName(rc), TCL_VOLATILE);
   394    397       return TCL_ERROR;
   395    398     }
   396    399   
   397    400     return TCL_OK;
   398    401   }
   399    402   
   400    403   static void xF5tFunction(
................................................................................
   723    726     Tcl_DecrRefCount(pEval);
   724    727   
   725    728     pInst->pContext->pCtx = pOldCtx;
   726    729     pInst->pContext->xToken = xOldToken;
   727    730     return rc;
   728    731   }
   729    732   
   730         -extern const char *sqlite3ErrName(int);
   731         -
   732    733   /*
   733    734   ** sqlite3_fts5_token TEXT START END POS
   734    735   */
   735    736   static int f5tTokenizerReturn(
   736    737     void * clientData,
   737    738     Tcl_Interp *interp,
   738    739     int objc,

Changes to ext/fts5/fts5_tokenize.c.

   525    525     const char **azArg, int nArg,
   526    526     Fts5Tokenizer **ppOut
   527    527   ){
   528    528     fts5_api *pApi = (fts5_api*)pCtx;
   529    529     int rc = SQLITE_OK;
   530    530     PorterTokenizer *pRet;
   531    531     void *pUserdata = 0;
          532  +  const char *zBase = "unicode61";
          533  +
          534  +  if( nArg>0 ){
          535  +    zBase = azArg[0];
          536  +  }
   532    537   
   533    538     pRet = (PorterTokenizer*)sqlite3_malloc(sizeof(PorterTokenizer));
   534    539     if( pRet ){
   535    540       memset(pRet, 0, sizeof(PorterTokenizer));
   536         -    rc = pApi->xFindTokenizer(pApi, "unicode61", &pUserdata, &pRet->tokenizer);
          541  +    rc = pApi->xFindTokenizer(pApi, zBase, &pUserdata, &pRet->tokenizer);
   537    542     }else{
   538    543       rc = SQLITE_NOMEM;
   539    544     }
   540    545     if( rc==SQLITE_OK ){
   541    546       rc = pRet->tokenizer.xCreate(pUserdata, 0, 0, &pRet->pTokenizer);
   542    547     }
   543    548   

Changes to ext/fts5/test/fts5aa.test.

   293    293   } {}
   294    294   
   295    295   do_catchsql_test 12.2 {
   296    296     SELECT t2 FROM t2 WHERE t2 MATCH '*stuff'
   297    297   } {1 {unknown special query: stuff}}
   298    298   
   299    299   do_test 12.3 {
   300         -  set res [db one { SELECT t2 FROM t2 WHERE t2 MATCH '* reads ' }]
          300  +  set res [db eval { SELECT t2 FROM t2 WHERE t2 MATCH '* reads ' }]
   301    301     string is integer $res
   302    302   } {1}
   303    303   
   304    304   #-------------------------------------------------------------------------
   305    305   #
   306    306   reset_db
   307    307   do_execsql_test 13.1 {

Changes to ext/fts5/test/fts5al.test.

   263    263   do_execsql_test 4.3.3 {
   264    264     SELECT *, rank FROM t3
   265    265     WHERE t3 MATCH 'a' AND rank MATCH 'rowidmod(3)' 
   266    266     ORDER BY rank ASC
   267    267   } {
   268    268     {a three} 0 {a one} 1 {a four} 1 {a two} 2 {a five} 2 
   269    269   }
          270  +
          271  +do_catchsql_test 4.4.3 {
          272  +  SELECT *, rank FROM t3 WHERE t3 MATCH 'a' AND rank MATCH 'xyz(3)' 
          273  +} {1 {no such function: xyz}}
          274  +do_catchsql_test 4.4.4 {
          275  +  SELECT *, rank FROM t3 WHERE t3 MATCH 'a' AND rank MATCH NULL
          276  +} {1 {parse error in rank function: }}
          277  +
   270    278   
   271    279   
   272    280   finish_test
   273    281   

Added ext/fts5/test/fts5aux.test.

            1  +# 2014 Dec 20
            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  +#
           12  +# Tests focusing on the auxiliary function APIs.
           13  +#
           14  +
           15  +source [file join [file dirname [info script]] fts5_common.tcl]
           16  +set testprefix fts5aux
           17  +
           18  +proc inst {cmd i} { 
           19  +  $cmd xInst $i
           20  +}
           21  +sqlite3_fts5_create_function db inst inst
           22  +
           23  +proc colsize {cmd i} { 
           24  +  $cmd xColumnSize $i
           25  +}
           26  +sqlite3_fts5_create_function db colsize colsize
           27  +
           28  +do_execsql_test 1.0 {
           29  +  CREATE VIRTUAL TABLE f1 USING fts5(a, b);
           30  +  INSERT INTO f1 VALUES('one two', 'two one zero');
           31  +}
           32  +
           33  +do_catchsql_test 1.1 {
           34  +  SELECT inst(f1, -1) FROM f1 WHERE f1 MATCH 'two';
           35  +} {1 SQLITE_RANGE}
           36  +do_catchsql_test 1.2 {
           37  +  SELECT inst(f1, 0) FROM f1 WHERE f1 MATCH 'two';
           38  +} {0 {{0 0 1}}}
           39  +do_catchsql_test 1.3 {
           40  +  SELECT inst(f1, 1) FROM f1 WHERE f1 MATCH 'two';
           41  +} {0 {{0 1 0}}}
           42  +do_catchsql_test 1.4 {
           43  +  SELECT inst(f1, 2) FROM f1 WHERE f1 MATCH 'two';
           44  +} {1 SQLITE_RANGE}
           45  +
           46  +do_catchsql_test 2.1 {
           47  +  SELECT colsize(f1, 2) FROM f1 WHERE f1 MATCH 'two';
           48  +} {1 SQLITE_RANGE}
           49  +
           50  +do_execsql_test 2.2 {
           51  +  SELECT colsize(f1, 0), colsize(f1, 1) FROM f1 WHERE f1 MATCH 'zero';
           52  +} {2 3}
           53  +
           54  +
           55  +
           56  +finish_test
           57  +

Changes to ext/fts5/test/fts5content.test.

   210    210     DELETE FROM x2 WHERE "key col" = 1;
   211    211     INSERT INTO t2(t2, rowid, a, c) VALUES('delete', 1, 'a b', 'e f');
   212    212     INSERT INTO t2(t2) VALUES('integrity-check');
   213    213   }
   214    214   
   215    215   do_execsql_test 4.8 { SELECT rowid FROM t2 WHERE t2 MATCH 'b'} {}
   216    216   do_execsql_test 4.9 { SELECT rowid FROM t2 WHERE t2 MATCH 'y'} {-40}
          217  +
          218  +#-------------------------------------------------------------------------
          219  +# Test that if the 'rowid' field of a 'delete' is not an integer, no
          220  +# changes are made to the FTS index.
          221  +#
          222  +do_execsql_test 5.0 {
          223  +  CREATE VIRTUAL TABLE t5 USING fts5(a, b, content=);
          224  +  INSERT INTO t5(rowid, a, b) VALUES(-1, 'one',   'two');
          225  +  INSERT INTO t5(rowid, a, b) VALUES( 0, 'three', 'four');
          226  +  INSERT INTO t5(rowid, a, b) VALUES( 1, 'five',  'six');
          227  +}
          228  +
          229  +set ::checksum [execsql {SELECT md5sum(id, block) FROM t5_data}]
          230  +
          231  +do_execsql_test 5.1 {
          232  +  INSERT INTO t5(t5, rowid, a, b) VALUES('delete', NULL, 'three', 'four');
          233  +  SELECT md5sum(id, block) FROM t5_data;
          234  +} $::checksum
   217    235   
   218    236   
   219    237   finish_test
   220    238   
   221    239   

Changes to ext/fts5/test/fts5corrupt.test.

    66     66     for {set i 0} {$i < 500} {incr i} {
    67     67       execsql { INSERT INTO t2 VALUES(rnddoc(50)) }
    68     68     }
    69     69     execsql { INSERT INTO t2(t2) VALUES('integrity-check') }
    70     70   } {}
    71     71   
    72     72   #--------------------------------------------------------------------
           73  +# A mundane test - missing row in the %_content table.
    73     74   #
           75  +do_execsql_test 3.0 {
           76  +  CREATE VIRTUAL TABLE t3 USING fts5(x);
           77  +  INSERT INTO t3 VALUES('one o');
           78  +  INSERT INTO t3 VALUES('two e');
           79  +  INSERT INTO t3 VALUES('three o');
           80  +  INSERT INTO t3 VALUES('four e');
           81  +  INSERT INTO t3 VALUES('five o');
           82  +}
           83  +do_execsql_test 3.1 {
           84  +  SELECT * FROM t3 WHERE t3 MATCH 'o'
           85  +} {{one o} {three o} {five o}}
           86  +
           87  +do_catchsql_test 3.1 {
           88  +  DELETE FROM t3_content WHERE rowid = 3;
           89  +  SELECT * FROM t3 WHERE t3 MATCH 'o';
           90  +} {1 {database disk image is malformed}}
    74     91   
    75     92   finish_test
    76     93   

Added ext/fts5/test/fts5doclist.test.

            1  +# 2015 April 21
            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  +#
           12  +# This test is focused on edge cases in the doclist format.
           13  +#
           14  +
           15  +source [file join [file dirname [info script]] fts5_common.tcl]
           16  +set testprefix fts5doclist
           17  +
           18  +
           19  +#-------------------------------------------------------------------------
           20  +# Create a table with 1000 columns. Then add some large documents to it.
           21  +# All text is in the right most column of the table.
           22  +#
           23  +do_test 1.0 {
           24  +  set cols [list]
           25  +  for {set i 0} {$i < 900} {incr i} { lappend cols "x$i" }
           26  +  execsql "CREATE VIRTUAL TABLE ccc USING fts5([join $cols ,])"
           27  +} {}
           28  +
           29  +db func rnddoc fts5_rnddoc 
           30  +do_execsql_test 1.1 {
           31  +  WITH ii(i) AS (SELECT 1 UNION SELECT i+1 FROM ii WHERE i<100)
           32  +  INSERT INTO ccc(x899) SELECT rnddoc(500) FROM ii;
           33  +}
           34  +
           35  +do_execsql_test 1.2 {
           36  +  INSERT INTO ccc(ccc) VALUES('integrity-check');
           37  +}
           38  +
           39  +
           40  +finish_test
           41  +

Changes to ext/fts5/test/fts5fault4.test.

    17     17   set testprefix fts5fault4
    18     18   
    19     19   # If SQLITE_ENABLE_FTS3 is defined, omit this file.
    20     20   ifcapable !fts5 {
    21     21     finish_test
    22     22     return
    23     23   }
           24  +
           25  +if 1 {
    24     26   
    25     27   #-------------------------------------------------------------------------
    26     28   # An OOM while dropping an fts5 table.
    27     29   #
    28     30   db func rnddoc fts5_rnddoc 
    29     31   do_test 1.0 {
    30     32     execsql { CREATE VIRTUAL TABLE xx USING fts5(x) }
................................................................................
    35     37     faultsim_restore_and_reopen
    36     38     execsql { SELECT * FROM xx }
    37     39   } -body {
    38     40     execsql { DROP TABLE xx }
    39     41   } -test {
    40     42     faultsim_test_result [list 0 {}]
    41     43   }
           44  +
           45  +#-------------------------------------------------------------------------
           46  +# An OOM within an "ORDER BY rank" query.
           47  +#
           48  +db func rnddoc fts5_rnddoc 
           49  +do_execsql_test 2.0 {
           50  +  CREATE VIRTUAL TABLE xx USING fts5(x);
           51  +  INSERT INTO xx VALUES ('abc ' || rnddoc(10));
           52  +  INSERT INTO xx VALUES ('abc abc' || rnddoc(9));
           53  +  INSERT INTO xx VALUES ('abc abc abc' || rnddoc(8));
           54  +} {}
           55  +faultsim_save_and_close
           56  +
           57  +do_faultsim_test 2 -faults oom-* -prep {
           58  +  faultsim_restore_and_reopen
           59  +  execsql { SELECT * FROM xx }
           60  +} -body {
           61  +  execsql { SELECT rowid FROM xx WHERE xx MATCH 'abc' ORDER BY rank }
           62  +} -test {
           63  +  faultsim_test_result [list 0 {3 2 1}]
           64  +}
           65  +
           66  +#-------------------------------------------------------------------------
           67  +# An OOM while "reseeking" an FTS cursor.
           68  +#
           69  +do_execsql_test 3.0 {
           70  +  CREATE VIRTUAL TABLE jj USING fts5(j);
           71  +  INSERT INTO jj(rowid, j) VALUES(101, 'm t w t f s s');
           72  +  INSERT INTO jj(rowid, j) VALUES(202, 't w t f s');
           73  +  INSERT INTO jj(rowid, j) VALUES(303, 'w t f');
           74  +  INSERT INTO jj(rowid, j) VALUES(404, 't');
           75  +}
           76  +faultsim_save_and_close
           77  +
           78  +do_faultsim_test 3 -faults oom-* -prep {
           79  +  faultsim_restore_and_reopen
           80  +  execsql { SELECT * FROM jj }
           81  +} -body {
           82  +  set res [list]
           83  +  db eval { SELECT rowid FROM jj WHERE jj MATCH 't' } {
           84  +    lappend res $rowid
           85  +    if {$rowid==303} {
           86  +      execsql { DELETE FROM jj WHERE rowid=404 }
           87  +    }
           88  +  }
           89  +  set res
           90  +} -test {
           91  +  faultsim_test_result [list 0 {101 202 303}]
           92  +}
           93  +
           94  +#-------------------------------------------------------------------------
           95  +# An OOM within a special "*reads" query.
           96  +#
           97  +reset_db
           98  +db func rnddoc fts5_rnddoc
           99  +do_execsql_test 4.0 {
          100  +  CREATE VIRTUAL TABLE x1 USING fts5(x);
          101  +  INSERT INTO x1(x1, rank) VALUES('pgsz', 32);
          102  +
          103  +  WITH ii(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM ii WHERE i<10 )
          104  +  INSERT INTO x1 SELECT rnddoc(5) FROM ii;
          105  +}
          106  +
          107  +set ::res [db eval {SELECT rowid, x1 FROM x1 WHERE x1 MATCH '*reads'}]
          108  +
          109  +do_faultsim_test 4 -faults oom-* -body {
          110  +  db eval {SELECT rowid, x, x1 FROM x1 WHERE x1 MATCH '*reads'}
          111  +} -test {
          112  +  faultsim_test_result {0 {0 {} 3}}
          113  +}
          114  +
          115  +#-------------------------------------------------------------------------
          116  +# An OOM within a query that uses a custom rank function.
          117  +#
          118  +reset_db
          119  +do_execsql_test 5.0 {
          120  +  PRAGMA encoding='utf16';
          121  +  CREATE VIRTUAL TABLE x2 USING fts5(x);
          122  +  INSERT INTO x2(rowid, x) VALUES(10, 'a b c'); -- 3
          123  +  INSERT INTO x2(rowid, x) VALUES(20, 'a b c'); -- 6
          124  +  INSERT INTO x2(rowid, x) VALUES(30, 'a b c'); -- 2
          125  +  INSERT INTO x2(rowid, x) VALUES(40, 'a b c'); -- 5
          126  +  INSERT INTO x2(rowid, x) VALUES(50, 'a b c'); -- 1
          127  +}
          128  +
          129  +proc rowidmod {cmd mod} { 
          130  +  set row [$cmd xRowid]
          131  +  expr {$row % $mod}
          132  +}
          133  +sqlite3_fts5_create_function db rowidmod rowidmod
          134  +
          135  +do_faultsim_test 5.1 -faults oom-* -body {
          136  +  db eval {
          137  +    SELECT rowid || '-' || rank FROM x2 WHERE x2 MATCH 'b' AND 
          138  +    rank MATCH "rowidmod('7')" ORDER BY rank
          139  +  }
          140  +} -test {
          141  +  faultsim_test_result {0 {50-1 30-2 10-3 40-5 20-6}}
          142  +}
          143  +
          144  +proc rowidprefix {cmd prefix} { 
          145  +  set row [$cmd xRowid]
          146  +  set {} "${row}-${prefix}"
          147  +}
          148  +sqlite3_fts5_create_function db rowidprefix rowidprefix
          149  +
          150  +set str [string repeat abcdefghijklmnopqrstuvwxyz 10]
          151  +do_faultsim_test 5.2 -faults oom-* -body {
          152  +  db eval "
          153  +    SELECT rank, x FROM x2 WHERE x2 MATCH 'b' AND 
          154  +    rank MATCH 'rowidprefix(''$::str'')'
          155  +    LIMIT 1
          156  +  "
          157  +} -test {
          158  +  faultsim_test_result "0 {10-$::str {a b c}}"
          159  +}
          160  +
          161  +}
          162  +
          163  +
          164  +#-------------------------------------------------------------------------
          165  +# OOM errors within auxiliary functions.
          166  +#
          167  +reset_db
          168  +do_execsql_test 6.0 {
          169  +  CREATE VIRTUAL TABLE x3 USING fts5(xxx);
          170  +  INSERT INTO x3 VALUES('a b c d c b a');
          171  +}
          172  +
          173  +do_faultsim_test 6.1 -faults oom-t* -body {
          174  +  db eval { SELECT highlight(x3, 0, '*', '*') FROM x3 WHERE x3 MATCH 'c' }
          175  +} -test {
          176  +  faultsim_test_result {0 {{a b *c* d *c* b a}}}
          177  +}
          178  +
          179  +proc firstinst {cmd} { 
          180  +  foreach {p c o} [$cmd xInst 0] {}
          181  +  expr $c*100 + $o
          182  +}
          183  +sqlite3_fts5_create_function db firstinst firstinst
          184  +
          185  +do_faultsim_test 6.2 -faults oom-t* -body {
          186  +  db eval { SELECT firstinst(x3) FROM x3 WHERE x3 MATCH 'c' }
          187  +} -test {
          188  +  faultsim_test_result {0 2} {1 SQLITE_NOMEM}
          189  +}
          190  +
          191  +
    42    192   
    43    193   
    44    194   finish_test
    45    195   

Added ext/fts5/test/fts5plan.test.

            1  +# 2014 Dec 20
            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  +#
           12  +# This file focuses on testing the planner (xBestIndex function).
           13  +#
           14  +
           15  +source [file join [file dirname [info script]] fts5_common.tcl]
           16  +set testprefix fts5plan
           17  +
           18  +do_execsql_test 1.0 {
           19  +  CREATE TABLE t1(x, y);
           20  +  CREATE VIRTUAL TABLE f1 USING fts5(ff);
           21  +}
           22  +
           23  +do_eqp_test 1.1 {
           24  +  SELECT * FROM t1, f1 WHERE f1 MATCH t1.x
           25  +} {
           26  +  0 0 0 {SCAN TABLE t1} 
           27  +  0 1 1 {SCAN TABLE f1 VIRTUAL TABLE INDEX 2:}
           28  +}
           29  +
           30  +do_eqp_test 1.2 {
           31  +  SELECT * FROM t1, f1 WHERE f1 > t1.x
           32  +} {
           33  +  0 0 1 {SCAN TABLE f1 VIRTUAL TABLE INDEX 1:}
           34  +  0 1 0 {SCAN TABLE t1} 
           35  +}
           36  +
           37  +do_eqp_test 1.3 {
           38  +  SELECT * FROM f1 WHERE f1 MATCH ? ORDER BY ff
           39  +} {
           40  +  0 0 0 {SCAN TABLE f1 VIRTUAL TABLE INDEX 2:}
           41  +  0 0 0 {USE TEMP B-TREE FOR ORDER BY}
           42  +}
           43  +
           44  +do_eqp_test 1.4 {
           45  +  SELECT * FROM f1 ORDER BY rank
           46  +} {
           47  +  0 0 0 {SCAN TABLE f1 VIRTUAL TABLE INDEX 1:}
           48  +  0 0 0 {USE TEMP B-TREE FOR ORDER BY}
           49  +}
           50  +
           51  +do_eqp_test 1.5 {
           52  +  SELECT * FROM f1 WHERE rank MATCH ?
           53  +} {
           54  +  0 0 0 {SCAN TABLE f1 VIRTUAL TABLE INDEX 1:}
           55  +}
           56  +
           57  +
           58  +
           59  +
           60  +finish_test
           61  +

Added ext/fts5/test/fts5rank.test.

            1  +# 2014 Dec 20
            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  +#
           12  +# This file focuses on testing queries that use the "rank" column.
           13  +#
           14  +
           15  +source [file join [file dirname [info script]] fts5_common.tcl]
           16  +set testprefix fts5rank
           17  +
           18  +
           19  +#-------------------------------------------------------------------------
           20  +# "ORDER BY rank" + highlight() + large poslists.
           21  +#
           22  +do_execsql_test 1.0 {
           23  +  CREATE VIRTUAL TABLE xyz USING fts5(z);
           24  +}
           25  +do_test 1.1 {
           26  +  set doc [string trim [string repeat "x y " 500]]
           27  +  execsql { INSERT INTO xyz VALUES($doc) }
           28  +} {}
           29  +do_execsql_test 1.2 {
           30  +  SELECT highlight(xyz, 0, '[', ']') FROM xyz WHERE xyz MATCH 'x' ORDER BY rank
           31  +} [list [string map {x [x]} $doc]]
           32  +
           33  +do_execsql_test 1.3 {
           34  +  SELECT highlight(xyz, 0, '[', ']') FROM xyz
           35  +  WHERE xyz MATCH 'x AND y' ORDER BY rank
           36  +} [list [string map {x [x] y [y]} $doc]]
           37  +
           38  +finish_test
           39  +

Changes to ext/fts5/test/fts5rebuild.test.

    42     42   } {1 {database disk image is malformed}}
    43     43   
    44     44   do_execsql_test 1.7 {
    45     45     INSERT INTO f1(f1) VALUES('rebuild');
    46     46     INSERT INTO f1(f1) VALUES('integrity-check');
    47     47   } {}
    48     48   
           49  +
           50  +#-------------------------------------------------------------------------
           51  +# Check that 'rebuild' may not be used with a contentless table.
           52  +#
           53  +do_execsql_test 2.1 {
           54  +  CREATE VIRTUAL TABLE nc USING fts5(doc, content=);
           55  +}
           56  +
           57  +do_catchsql_test 2.2 {
           58  +  INSERT INTO nc(nc) VALUES('rebuild');
           59  +} {1 {'rebuild' may not be used with a contentless fts5 table}}
    49     60   finish_test
    50     61   

Changes to ext/fts5/test/fts5restart.test.

    15     15   source [file join [file dirname [info script]] fts5_common.tcl]
    16     16   set testprefix fts5restart
    17     17   
    18     18   do_execsql_test 1.0 {
    19     19     CREATE VIRTUAL TABLE f1 USING fts5(ff);
    20     20   }
    21     21   
           22  +#-------------------------------------------------------------------------
           23  +# Run the 'optimize' command. Check that it does not disturb ongoing
           24  +# full-text queries.
           25  +#
    22     26   do_test 1.1 {
    23     27     for {set i 1} {$i < 1000} {incr i} {
    24     28       execsql { INSERT INTO f1 VALUES('a b c d e') }
    25     29       lappend lRowid $i
    26     30     }
    27     31   } {}
    28     32   
    29     33   do_execsql_test 1.2 {
    30     34     SELECT rowid FROM f1 WHERE f1 MATCH 'c';
    31     35   } $lRowid
    32     36   
    33         -breakpoint
    34     37   do_test 1.3 {
    35     38     set res [list]
    36     39     db eval { SELECT rowid FROM f1 WHERE f1 MATCH 'c' } {
    37     40       if {$rowid == 100} {
    38     41         execsql { INSERT INTO f1(f1) VALUES('optimize') }
    39     42       }
    40     43       lappend res $rowid
    41     44     }
    42     45     set res
    43     46   } $lRowid
           47  +
           48  +do_test 1.4.1 {
           49  +  sqlite3 db2 test.db
           50  +  set res [list]
           51  +  db2 eval { SELECT rowid FROM f1 WHERE f1 MATCH 'c' } {
           52  +    if {$rowid == 100} {
           53  +      set cres [catchsql { INSERT INTO f1(f1) VALUES('optimize') }]
           54  +    }
           55  +    lappend res $rowid
           56  +  }
           57  +  set res
           58  +} $lRowid
           59  +
           60  +do_test 1.4.2 {
           61  +  db2 close
           62  +  set cres
           63  +} {1 {database is locked}}
           64  +
           65  +#-------------------------------------------------------------------------
           66  +# Open a couple of cursors. Then close them in the same order.
           67  +#
           68  +do_test 2.1 {
           69  +  set ::s1 [sqlite3_prepare db "SELECT rowid FROM f1 WHERE f1 MATCH 'b'" -1 X]
           70  +  set ::s2 [sqlite3_prepare db "SELECT rowid FROM f1 WHERE f1 MATCH 'c'" -1 X]
           71  +
           72  +  sqlite3_step $::s1
           73  +} {SQLITE_ROW}
           74  +do_test 2.2 {
           75  +  sqlite3_step $::s2
           76  +} {SQLITE_ROW}
           77  +
           78  +do_test 2.1 {
           79  +  sqlite3_finalize $::s1
           80  +  sqlite3_finalize $::s2
           81  +} {SQLITE_OK}
           82  +
           83  +#-------------------------------------------------------------------------
           84  +# Copy data between two FTS5 tables.
           85  +#
           86  +do_execsql_test 3.1 {
           87  +  CREATE VIRTUAL TABLE f2 USING fts5(gg);
           88  +  INSERT INTO f2 SELECT ff FROM f1 WHERE f1 MATCH 'b+c+d';
           89  +}
           90  +do_execsql_test 3.2 {
           91  +  SELECT rowid FROM f2 WHERE f2 MATCH 'a+b+c+d+e'
           92  +} $lRowid
           93  +
           94  +#-------------------------------------------------------------------------
           95  +# Remove the row that an FTS5 cursor is currently pointing to. And 
           96  +# various other similar things. Check that this does not disturb 
           97  +# ongoing scans.
           98  +#
           99  +do_execsql_test 4.0 {
          100  +  CREATE VIRTUAL TABLE n4 USING fts5(n);
          101  +  INSERT INTO n4(rowid, n) VALUES(100, '1 2 3 4 5');
          102  +  INSERT INTO n4(rowid, n) VALUES(200, '1 2 3 4');
          103  +  INSERT INTO n4(rowid, n) VALUES(300, '2 3 4');
          104  +  INSERT INTO n4(rowid, n) VALUES(400, '2 3');
          105  +  INSERT INTO n4(rowid, n) VALUES(500, '3');
          106  +}
          107  +
          108  +do_test 4.1 {
          109  +  set res [list]
          110  +  db eval { SELECT rowid FROM n4 WHERE n4 MATCH '3' } {
          111  +    if {$rowid==300} {
          112  +      execsql { DELETE FROM n4 WHERE rowid=300 }
          113  +    }
          114  +    lappend res $rowid
          115  +  }
          116  +  set res
          117  +} {100 200 300 400 500}
          118  +
          119  +do_test 4.2 {
          120  +  execsql { INSERT INTO n4(rowid, n) VALUES(300, '2 3 4') }
          121  +  set res [list]
          122  +  db eval { SELECT rowid FROM n4 WHERE n4 MATCH '3' ORDER BY rowid DESC} {
          123  +    if {$rowid==300} {
          124  +      execsql { DELETE FROM n4 WHERE rowid=300 }
          125  +    }
          126  +    lappend res $rowid
          127  +  }
          128  +  set res
          129  +} {500 400 300 200 100}
          130  +
          131  +do_test 4.3 {
          132  +  execsql { INSERT INTO n4(rowid, n) VALUES(300, '2 3 4') }
          133  +  set res [list]
          134  +  db eval { SELECT rowid FROM n4 WHERE n4 MATCH '3' ORDER BY rowid DESC} {
          135  +    if {$rowid==300} {
          136  +      execsql { DELETE FROM n4  }
          137  +    }
          138  +    lappend res $rowid
          139  +  }
          140  +  set res
          141  +} {500 400 300}
    44    142   
    45    143   
    46    144   
    47    145   finish_test
    48    146   

Changes to ext/fts5/test/fts5tokenizer.test.

    33     33     DROP TABLE ft1;
    34     34   }
    35     35   do_execsql_test 1.4 {
    36     36     CREATE VIRTUAL TABLE ft1 USING fts5(x, tokenize = 'porter ascii');
    37     37     DROP TABLE ft1;
    38     38   }
    39     39   
           40  +do_catchsql_test 1.5 {
           41  +  CREATE VIRTUAL TABLE ft1 USING fts5(x, tokenize = 'nosuch');
           42  +} {1 {no such tokenizer: nosuch}}
           43  +
           44  +do_catchsql_test 1.6 {
           45  +  CREATE VIRTUAL TABLE ft1 USING fts5(x, tokenize = 'porter nosuch');
           46  +} {1 {error in tokenizer constructor}}
           47  +
    40     48   do_execsql_test 2.0 {
    41     49     CREATE VIRTUAL TABLE ft1 USING fts5(x, tokenize=porter);
    42     50     INSERT INTO ft1 VALUES('embedded databases');
    43     51   }
    44     52   do_execsql_test 2.1 { SELECT rowid FROM ft1 WHERE ft1 MATCH 'embedding' } 1
    45     53   do_execsql_test 2.2 { SELECT rowid FROM ft1 WHERE ft1 MATCH 'database' } 1
    46     54   do_execsql_test 2.3 {