/ Check-in [07f70955]
Login

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

Overview
Comment:Fix an fts5 bug in handling writes while there are active cursors.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | fts5
Files: files | file ages | folders
SHA1: 07f70955392697556ca2951c9b6c3a5204cd5ec0
User & Date: dan 2015-04-28 20:24:50
Context
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
18:35
Improve coverage of fts5 tests. check-in: 8e8136f2 user: dan tags: fts5
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to ext/fts5/fts5.c.

   182    182   /*
   183    183   ** Values for Fts5Cursor.csrflags
   184    184   */
   185    185   #define FTS5CSR_REQUIRE_CONTENT   0x01
   186    186   #define FTS5CSR_REQUIRE_DOCSIZE   0x02
   187    187   #define FTS5CSR_EOF               0x04
   188    188   #define FTS5CSR_FREE_ZRANK        0x08
          189  +#define FTS5CSR_REQUIRE_RESEEK    0x10
   189    190   
   190    191   /*
   191    192   ** Macros to Set(), Clear() and Test() cursor flags.
   192    193   */
   193    194   #define CsrFlagSet(pCsr, flag)   ((pCsr)->csrflags |= (flag))
   194    195   #define CsrFlagClear(pCsr, flag) ((pCsr)->csrflags &= ~(flag))
   195    196   #define CsrFlagTest(pCsr, flag)  ((pCsr)->csrflags & (flag))
................................................................................
   265    266     return pTab->pConfig->eContent==FTS5_CONTENT_NONE;
   266    267   }
   267    268   
   268    269   /*
   269    270   ** Delete a virtual table handle allocated by fts5InitVtab(). 
   270    271   */
   271    272   static void fts5FreeVtab(Fts5Table *pTab){
   272         -  int rc = SQLITE_OK;
   273    273     if( pTab ){
   274    274       sqlite3Fts5IndexClose(pTab->pIndex);
   275    275       sqlite3Fts5StorageClose(pTab->pStorage);
   276    276       sqlite3Fts5ConfigFree(pTab->pConfig);
   277    277       sqlite3_free(pTab);
   278    278     }
   279         -  return rc;
   280    279   }
   281    280   
   282    281   /*
   283    282   ** The xDisconnect() virtual table method.
   284    283   */
   285    284   static int fts5DisconnectMethod(sqlite3_vtab *pVtab){
   286    285     fts5FreeVtab((Fts5Table*)pVtab);
................................................................................
   603    602   
   604    603       pSorter->aPoslist = a;
   605    604       fts5CsrNewrow(pCsr);
   606    605     }
   607    606   
   608    607     return rc;
   609    608   }
          609  +
          610  +
          611  +/*
          612  +** Set the FTS5CSR_REQUIRE_RESEEK flag on all FTS5_PLAN_MATCH cursors 
          613  +** open on table pTab.
          614  +*/
          615  +static void fts5TripCursors(Fts5Table *pTab){
          616  +  Fts5Cursor *pCsr;
          617  +  for(pCsr=pTab->pGlobal->pCsr; pCsr; pCsr=pCsr->pNext){
          618  +    if( FTS5_PLAN(pCsr->idxNum)==FTS5_PLAN_MATCH
          619  +     && pCsr->base.pVtab==(sqlite3_vtab*)pTab 
          620  +    ){
          621  +      CsrFlagSet(pCsr, FTS5CSR_REQUIRE_RESEEK);
          622  +    }
          623  +  }
          624  +}
          625  +
          626  +/*
          627  +** If the REQUIRE_RESEEK flag is set on the cursor passed as the first
          628  +** argument, close and reopen all Fts5IndexIter iterators that the cursor 
          629  +** is using. Then attempt to move the cursor to a rowid equal to or laster
          630  +** (in the cursors sort order - ASC or DESC) than the current rowid. 
          631  +**
          632  +** If the new rowid is not equal to the old, set output parameter *pbSkip
          633  +** to 1 before returning. Otherwise, leave it unchanged.
          634  +**
          635  +** Return SQLITE_OK if successful or if no reseek was required, or an 
          636  +** error code if an error occurred.
          637  +*/
          638  +static int fts5CursorReseek(Fts5Cursor *pCsr, int *pbSkip){
          639  +  int rc = SQLITE_OK;
          640  +  assert( *pbSkip==0 );
          641  +  if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_RESEEK) ){
          642  +    Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
          643  +    int bDesc = ((pCsr->idxNum & FTS5_ORDER_DESC) ? 1 : 0);
          644  +    i64 iRowid = sqlite3Fts5ExprRowid(pCsr->pExpr);
          645  +
          646  +    rc = sqlite3Fts5ExprFirst(pCsr->pExpr, pTab->pIndex, bDesc);
          647  +    while( rc==SQLITE_OK && sqlite3Fts5ExprEof(pCsr->pExpr)==0 ){
          648  +      i64 ii = sqlite3Fts5ExprRowid(pCsr->pExpr);
          649  +      if( ii==iRowid ) break;
          650  +      if( (bDesc && ii<iRowid) || (bDesc==0 && ii>iRowid) ){
          651  +        *pbSkip = 1;
          652  +        break;
          653  +      }
          654  +      rc = sqlite3Fts5ExprNext(pCsr->pExpr);
          655  +    }
          656  +
          657  +    CsrFlagClear(pCsr, FTS5CSR_REQUIRE_RESEEK);
          658  +    fts5CsrNewrow(pCsr);
          659  +    if( sqlite3Fts5ExprEof(pCsr->pExpr) ){
          660  +      CsrFlagSet(pCsr, FTS5CSR_EOF);
          661  +    }
          662  +  }
          663  +  return rc;
          664  +}
          665  +
   610    666   
   611    667   /*
   612    668   ** Advance the cursor to the next row in the table that matches the 
   613    669   ** search criteria.
   614    670   **
   615    671   ** Return SQLITE_OK if nothing goes wrong.  SQLITE_OK is returned
   616    672   ** even if we reach end-of-file.  The fts5EofMethod() will be called
   617    673   ** subsequently to determine whether or not an EOF was hit.
   618    674   */
   619    675   static int fts5NextMethod(sqlite3_vtab_cursor *pCursor){
   620    676     Fts5Cursor *pCsr = (Fts5Cursor*)pCursor;
   621    677     int ePlan = FTS5_PLAN(pCsr->idxNum);
   622         -  int rc = SQLITE_OK;
          678  +  int bSkip = 0;
          679  +  int rc;
          680  +
          681  +  if( (rc = fts5CursorReseek(pCsr, &bSkip)) || bSkip ) return rc;
   623    682   
   624    683     switch( ePlan ){
   625    684       case FTS5_PLAN_MATCH:
   626    685       case FTS5_PLAN_SOURCE:
   627    686         rc = sqlite3Fts5ExprNext(pCsr->pExpr);
   628    687         if( sqlite3Fts5ExprEof(pCsr->pExpr) ){
   629    688           CsrFlagSet(pCsr, FTS5CSR_EOF);
................................................................................
  1152   1211   
  1153   1212     eType0 = sqlite3_value_type(apVal[0]);
  1154   1213     eConflict = sqlite3_vtab_on_conflict(pConfig->db);
  1155   1214   
  1156   1215     assert( eType0==SQLITE_INTEGER || eType0==SQLITE_NULL );
  1157   1216     assert( pVtab->zErrMsg==0 );
  1158   1217   
         1218  +  fts5TripCursors(pTab);
  1159   1219     if( rc==SQLITE_OK && eType0==SQLITE_INTEGER ){
  1160   1220       if( fts5IsContentless(pTab) ){
  1161   1221         pTab->base.zErrMsg = sqlite3_mprintf(
  1162   1222             "cannot %s contentless fts5 table: %s", 
  1163   1223             (nArg>1 ? "UPDATE" : "DELETE from"), pConfig->zName
  1164   1224         );
  1165   1225         rc = SQLITE_ERROR;
................................................................................
  1192   1252   /*
  1193   1253   ** Implementation of xSync() method. 
  1194   1254   */
  1195   1255   static int fts5SyncMethod(sqlite3_vtab *pVtab){
  1196   1256     int rc;
  1197   1257     Fts5Table *pTab = (Fts5Table*)pVtab;
  1198   1258     fts5CheckTransactionState(pTab, FTS5_SYNC, 0);
         1259  +  fts5TripCursors(pTab);
  1199   1260     rc = sqlite3Fts5StorageSync(pTab->pStorage, 1);
  1200   1261     return rc;
  1201   1262   }
  1202   1263   
  1203   1264   /*
  1204   1265   ** Implementation of xBegin() method. 
  1205   1266   */
................................................................................
  1713   1774   ** The xSavepoint() method.
  1714   1775   **
  1715   1776   ** Flush the contents of the pending-terms table to disk.
  1716   1777   */
  1717   1778   static int fts5SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){
  1718   1779     Fts5Table *pTab = (Fts5Table*)pVtab;
  1719   1780     fts5CheckTransactionState(pTab, FTS5_SAVEPOINT, iSavepoint);
         1781  +  fts5TripCursors(pTab);
  1720   1782     return sqlite3Fts5StorageSync(pTab->pStorage, 0);
  1721   1783   }
  1722   1784   
  1723   1785   /*
  1724   1786   ** The xRelease() method.
  1725   1787   **
  1726   1788   ** This is a no-op.
  1727   1789   */
  1728   1790   static int fts5ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){
  1729   1791     Fts5Table *pTab = (Fts5Table*)pVtab;
  1730   1792     fts5CheckTransactionState(pTab, FTS5_RELEASE, iSavepoint);
         1793  +  fts5TripCursors(pTab);
  1731   1794     return sqlite3Fts5StorageSync(pTab->pStorage, 0);
  1732   1795   }
  1733   1796   
  1734   1797   /*
  1735   1798   ** The xRollbackTo() method.
  1736   1799   **
  1737   1800   ** Discard the contents of the pending terms table.
  1738   1801   */
  1739   1802   static int fts5RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){
  1740   1803     Fts5Table *pTab = (Fts5Table*)pVtab;
  1741   1804     fts5CheckTransactionState(pTab, FTS5_ROLLBACKTO, iSavepoint);
         1805  +  fts5TripCursors(pTab);
  1742   1806     return sqlite3Fts5StorageRollback(pTab->pStorage);
  1743   1807   }
  1744   1808   
  1745   1809   /*
  1746   1810   ** Register a new auxiliary function with global context pGlobal.
  1747   1811   */
  1748   1812   static int fts5CreateAux(

Changes to ext/fts5/fts5_expr.c.

   772    772     int i, j;
   773    773     int rc = SQLITE_OK;
   774    774   
   775    775     for(i=0; rc==SQLITE_OK && i<pNear->nPhrase; i++){
   776    776       pPhrase = pNear->apPhrase[i];
   777    777       for(j=0; j<pPhrase->nTerm; j++){
   778    778         pTerm = &pPhrase->aTerm[j];
          779  +      if( pTerm->pIter ){
          780  +        sqlite3Fts5IterClose(pTerm->pIter);
          781  +        pTerm->pIter = 0;
          782  +      }
   779    783         rc = sqlite3Fts5IndexQuery(
   780    784             pExpr->pIndex, pTerm->zTerm, strlen(pTerm->zTerm),
   781    785             (pTerm->bPrefix ? FTS5INDEX_QUERY_PREFIX : 0) |
   782    786             (pExpr->bDesc ? FTS5INDEX_QUERY_DESC : 0),
   783    787             &pTerm->pIter
   784    788         );
   785    789         assert( rc==SQLITE_OK || pTerm->pIter==0 );

Added ext/fts5/test/fts5restart.test.

            1  +# 2015 April 28
            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 fts5restart
           17  +
           18  +do_execsql_test 1.0 {
           19  +  CREATE VIRTUAL TABLE f1 USING fts5(ff);
           20  +}
           21  +
           22  +do_test 1.1 {
           23  +  for {set i 1} {$i < 1000} {incr i} {
           24  +    execsql { INSERT INTO f1 VALUES('a b c d e') }
           25  +    lappend lRowid $i
           26  +  }
           27  +} {}
           28  +
           29  +do_execsql_test 1.2 {
           30  +  SELECT rowid FROM f1 WHERE f1 MATCH 'c';
           31  +} $lRowid
           32  +
           33  +breakpoint
           34  +do_test 1.3 {
           35  +  set res [list]
           36  +  db eval { SELECT rowid FROM f1 WHERE f1 MATCH 'c' } {
           37  +    if {$rowid == 100} {
           38  +      execsql { INSERT INTO f1(f1) VALUES('optimize') }
           39  +    }
           40  +    lappend res $rowid
           41  +  }
           42  +  set res
           43  +} $lRowid
           44  +
           45  +
           46  +
           47  +finish_test
           48  +