/ Check-in [53b80a6d]
Login

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

Overview
Comment:Fix a problem with OOM handling when setting an fts5 configuration option.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | fts5
Files: files | file ages | folders
SHA1: 53b80a6d054a1c87311b3dc1c2bcfcc1b676b05a
User & Date: dan 2016-03-26 20:11:04
Context
2016-03-28
15:06
Integrate the vcreate-stmt branch into this one. check-in: 06039d90 user: dan tags: fts5
2016-03-26
20:11
Fix a problem with OOM handling when setting an fts5 configuration option. check-in: 53b80a6d user: dan tags: fts5
15:36
More changes to the shellN.test scripts to get them working on all variations of Windows. check-in: 8213c2f5 user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Show Whitespace Changes Patch

Changes to ext/fts5/fts5Int.h.

   469    469   
   470    470   /* 
   471    471   ** Called during virtual module initialization to register UDF 
   472    472   ** fts5_decode() with SQLite 
   473    473   */
   474    474   int sqlite3Fts5IndexInit(sqlite3*);
   475    475   
   476         -int sqlite3Fts5IndexSetCookie(Fts5Index*, int);
          476  +int sqlite3Fts5IndexIncrCookie(Fts5Index*);
   477    477   
   478    478   /*
   479    479   ** Return the total number of entries read from the %_data table by 
   480    480   ** this connection since it was created.
   481    481   */
   482    482   int sqlite3Fts5IndexReads(Fts5Index *p);
   483    483   
   484    484   int sqlite3Fts5IndexReinit(Fts5Index *p);
   485    485   int sqlite3Fts5IndexOptimize(Fts5Index *p);
   486    486   int sqlite3Fts5IndexMerge(Fts5Index *p, int nMerge);
   487         -int sqlite3Fts5IndexReset(Fts5Index *p);
          487  +int sqlite3Fts5IndexNewTrans(Fts5Index *p);
   488    488   
   489    489   int sqlite3Fts5IndexLoadConfig(Fts5Index *p);
          490  +
          491  +void sqlite3Fts5IndexCloseReader(Fts5Index*);
   490    492   
   491    493   /*
   492    494   ** End of interface to code in fts5_index.c.
   493    495   **************************************************************************/
   494    496   
   495    497   /**************************************************************************
   496    498   ** Interface to code in fts5_varint.c. 

Changes to ext/fts5/fts5_index.c.

     8      8   **    May you find forgiveness for yourself and forgive others.
     9      9   **    May you share freely, never taking more than you give.
    10     10   **
    11     11   ******************************************************************************
    12     12   **
    13     13   ** Low level access to the FTS index stored in the database file. The 
    14     14   ** routines in this file file implement all read and write access to the
    15         -** %_data table. Other parts of the system access this functionality via
    16         -** the interface defined in fts5Int.h.
           15  +** %_data and %_idx tables. Other parts of the system access this 
           16  +** functionality via the interface defined in fts5Int.h.
    17     17   */
    18     18   
    19     19   
    20     20   #include "fts5Int.h"
    21     21   
    22     22   /*
    23     23   ** Overview:
    24     24   **
    25         -** The %_data table contains all the FTS indexes for an FTS5 virtual table.
    26         -** As well as the main term index, there may be up to 31 prefix indexes.
    27         -** The format is similar to FTS3/4, except that:
           25  +** The %_data table contains the FTS index for an FTS5 virtual table.
           26  +** All entries, for terms and prefixes, are stored in a single data
           27  +** structure. The format is similar to FTS3/4, but differs in the 
           28  +** following ways:
    28     29   **
    29     30   **   * all segment b-tree leaf data is stored in fixed size page records 
    30     31   **     (e.g. 1000 bytes). A single doclist may span multiple pages. Care is 
    31     32   **     taken to ensure it is possible to iterate in either direction through 
    32     33   **     the entries in a doclist, or to seek to a specific entry within a 
    33     34   **     doclist, without loading it into memory.
    34     35   **
................................................................................
    35     36   **   * large doclists that span many pages have associated "doclist index"
    36     37   **     records that contain a copy of the first rowid on each page spanned by
    37     38   **     the doclist. This is used to speed up seek operations, and merges of
    38     39   **     large doclists with very small doclists.
    39     40   **
    40     41   **   * extra fields in the "structure record" record the state of ongoing
    41     42   **     incremental merge operations.
    42         -**
    43     43   */
    44     44   
    45     45   
    46         -#define FTS5_OPT_WORK_UNIT  1000  /* Number of leaf pages per optimize step */
    47         -#define FTS5_WORK_UNIT      64    /* Number of leaf pages in unit of work */
    48         -
    49         -#define FTS5_MIN_DLIDX_SIZE 4     /* Add dlidx if this many empty pages */
    50         -
    51         -#define FTS5_MAIN_PREFIX '0'
    52         -
    53         -#if FTS5_MAX_PREFIX_INDEXES > 31
    54         -# error "FTS5_MAX_PREFIX_INDEXES is too large"
    55         -#endif
    56         -
    57     46   /*
    58         -** Details:
           47  +** Contents of %_data table:
    59     48   **
    60     49   ** The %_data table managed by this module,
    61     50   **
    62     51   **     CREATE TABLE %_data(id INTEGER PRIMARY KEY, block BLOB);
    63     52   **
    64         -** , contains the following 5 types of records. See the comments surrounding
           53  +** , contains the following 4 types of records. See the comments surrounding
    65     54   ** the FTS5_*_ROWID macros below for a description of how %_data rowids are 
    66         -** assigned to each fo them.
           55  +** assigned to each of them.
    67     56   **
    68     57   ** 1. Structure Records:
    69     58   **
    70     59   **   The set of segments that make up an index - the index structure - are
    71     60   **   recorded in a single record within the %_data table. The record consists
    72     61   **   of a single 32-bit configuration cookie value followed by a list of 
    73     62   **   SQLite varints. If the FTS table features more than one index (because
................................................................................
   172    161   **
   173    162   **       + the first term on each page is stored in the same way as the
   174    163   **         very first term of the segment:
   175    164   **
   176    165   **             varint : size of first term
   177    166   **             blob:    first term data
   178    167   **
   179         -** 5. Segment doclist indexes:
          168  +** 4. Segment doclist indexes:
   180    169   **
   181    170   **   Doclist indexes are themselves b-trees, however they usually consist of
   182    171   **   a single leaf record only. The format of each doclist index leaf page 
   183    172   **   is:
   184    173   **
   185    174   **     * Flags byte. Bits are:
   186    175   **         0x01: Clear if leaf is also the root page, otherwise set.
................................................................................
   202    191   **
   203    192   **     * Copy of first rowid on page indicated by previous field. As a varint.
   204    193   **
   205    194   **     * A list of delta-encoded varints - the first rowid on each subsequent
   206    195   **       child page. 
   207    196   **
   208    197   */
          198  +
          199  +
          200  +#define FTS5_OPT_WORK_UNIT  1000  /* Number of leaf pages per optimize step */
          201  +#define FTS5_WORK_UNIT      64    /* Number of leaf pages in unit of work */
          202  +#define FTS5_MIN_DLIDX_SIZE 4     /* Add dlidx if this many empty pages */
          203  +
          204  +/* All entries for regular terms in the FTS index are prefixed with '0'.
          205  +** Entries for the first prefix index are prefixed with '1'. And so on, 
          206  +** up to ('0'+31).  */
          207  +#define FTS5_MAIN_PREFIX '0'
          208  +
          209  +#if FTS5_MAX_PREFIX_INDEXES > 31
          210  +# error "FTS5_MAX_PREFIX_INDEXES is too large"
          211  +#endif
          212  +
   209    213   
   210    214   /*
   211    215   ** Rowids for the averages and structure records in the %_data table.
   212    216   */
   213    217   #define FTS5_AVERAGES_ROWID     1    /* Rowid used for the averages record */
   214    218   #define FTS5_STRUCTURE_ROWID   10    /* The structure record */
   215    219   
................................................................................
   301    305     sqlite3_stmt *pWriter;          /* "INSERT ... %_data VALUES(?,?)" */
   302    306     sqlite3_stmt *pDeleter;         /* "DELETE FROM %_data ... id>=? AND id<=?" */
   303    307     sqlite3_stmt *pIdxWriter;       /* "INSERT ... %_idx VALUES(?,?,?,?)" */
   304    308     sqlite3_stmt *pIdxDeleter;      /* "DELETE FROM %_idx WHERE segid=? */
   305    309     sqlite3_stmt *pIdxSelect;
   306    310     int nRead;                      /* Total number of blocks read */
   307    311   
   308         -  sqlite3_stmt *pDataVersion;
          312  +  /* In-memory cache of the 'structure' record */
          313  +  sqlite3_stmt *pDataVersion;     /* PRAGMA <db>.data_version */
   309    314     i64 iStructVersion;             /* data_version when pStruct read */
   310    315     Fts5Structure *pStruct;         /* Current db structure (or NULL) */
   311    316   };
   312    317   
   313    318   struct Fts5DoclistIter {
   314    319     u8 *aEof;                       /* Pointer to 1 byte past end of doclist */
   315    320   
................................................................................
   616    621     fts5GetVarint32(&pLeaf->p[pLeaf->szLeaf], ret);
   617    622     return ret;
   618    623   }
   619    624   
   620    625   /*
   621    626   ** Close the read-only blob handle, if it is open.
   622    627   */
   623         -static void fts5CloseReader(Fts5Index *p){
          628  +void sqlite3Fts5IndexCloseReader(Fts5Index *p){
   624    629     if( p->pReader ){
   625    630       sqlite3_blob *pReader = p->pReader;
   626    631       p->pReader = 0;
   627    632       sqlite3_blob_close(pReader);
   628    633     }
   629    634   }
   630    635   
................................................................................
   646    651         ** is required.  */
   647    652         sqlite3_blob *pBlob = p->pReader;
   648    653         p->pReader = 0;
   649    654         rc = sqlite3_blob_reopen(pBlob, iRowid);
   650    655         assert( p->pReader==0 );
   651    656         p->pReader = pBlob;
   652    657         if( rc!=SQLITE_OK ){
   653         -        fts5CloseReader(p);
          658  +        sqlite3Fts5IndexCloseReader(p);
   654    659         }
   655    660         if( rc==SQLITE_ABORT ) rc = SQLITE_OK;
   656    661       }
   657    662   
   658    663       /* If the blob handle is not open at this point, open it and seek 
   659    664       ** to the requested entry.  */
   660    665       if( p->pReader==0 && rc==SQLITE_OK ){
................................................................................
  1002   1007         iVersion = sqlite3_column_int64(p->pDataVersion, 0);
  1003   1008       }
  1004   1009       p->rc = sqlite3_reset(p->pDataVersion);
  1005   1010     }
  1006   1011   
  1007   1012     return iVersion;
  1008   1013   }
         1014  +
         1015  +/*
         1016  +** If there is currently no cache of the index structure in memory, load
         1017  +** one from the database.
         1018  +*/
         1019  +static void fts5StructureCache(Fts5Index *p){
         1020  +  if( p->pStruct==0 ){
         1021  +    p->iStructVersion = fts5IndexDataVersion(p);
         1022  +    if( p->rc==SQLITE_OK ){
         1023  +      p->pStruct = fts5StructureReadUncached(p);
         1024  +    }
         1025  +  }
         1026  +}
  1009   1027   
  1010   1028   /*
  1011   1029   ** Read, deserialize and return the structure record.
  1012   1030   **
  1013   1031   ** The Fts5Structure.aLevel[] and each Fts5StructureLevel.aSeg[] array
  1014   1032   ** are over-allocated as described for function fts5StructureDecode() 
  1015   1033   ** above.
  1016   1034   **
  1017   1035   ** If an error occurs, NULL is returned and an error code left in the
  1018   1036   ** Fts5Index handle. If an error has already occurred when this function
  1019   1037   ** is called, it is a no-op.
  1020   1038   */
  1021   1039   static Fts5Structure *fts5StructureRead(Fts5Index *p){
  1022         -
  1023         -  if( p->pStruct==0 ){
  1024         -    p->iStructVersion = fts5IndexDataVersion(p);
  1025         -    if( p->rc==SQLITE_OK ){
  1026         -      p->pStruct = fts5StructureReadUncached(p);
  1027         -    }
  1028         -  }
         1040  +  fts5StructureCache(p);
  1029   1041   
  1030   1042   #if 0
  1031   1043     else{
  1032   1044       Fts5Structure *pTest = fts5StructureReadUncached(p);
  1033   1045       if( pTest ){
  1034   1046         int i, j;
  1035   1047         assert_nc( p->pStruct->nSegment==pTest->nSegment );
................................................................................
  4392   4404   
  4393   4405   static int fts5IndexReturn(Fts5Index *p){
  4394   4406     int rc = p->rc;
  4395   4407     p->rc = SQLITE_OK;
  4396   4408     return rc;
  4397   4409   }
  4398   4410   
  4399         -typedef struct Fts5FlushCtx Fts5FlushCtx;
  4400         -struct Fts5FlushCtx {
  4401         -  Fts5Index *pIdx;
  4402         -  Fts5SegWriter writer; 
  4403         -};
  4404         -
  4405   4411   /*
  4406   4412   ** Buffer aBuf[] contains a list of varints, all small enough to fit
  4407   4413   ** in a 32-bit integer. Return the size of the largest prefix of this 
  4408   4414   ** list nMax bytes or less in size.
  4409   4415   */
  4410   4416   static int fts5PoslistPrefix(const u8 *aBuf, int nMax){
  4411   4417     int ret;
................................................................................
  5100   5106   
  5101   5107   /*
  5102   5108   ** Commit data to disk.
  5103   5109   */
  5104   5110   int sqlite3Fts5IndexSync(Fts5Index *p, int bCommit){
  5105   5111     assert( p->rc==SQLITE_OK );
  5106   5112     fts5IndexFlush(p);
  5107         -  if( bCommit ) fts5CloseReader(p);
         5113  +  if( bCommit ) sqlite3Fts5IndexCloseReader(p);
  5108   5114     return fts5IndexReturn(p);
  5109   5115   }
  5110   5116   
  5111   5117   /*
  5112   5118   ** Discard any data stored in the in-memory hash tables. Do not write it
  5113   5119   ** to the database. Additionally, assume that the contents of the %_data
  5114   5120   ** table may have changed on disk. So any in-memory caches of %_data 
  5115   5121   ** records must be invalidated.
  5116   5122   */
  5117   5123   int sqlite3Fts5IndexRollback(Fts5Index *p){
  5118         -  fts5CloseReader(p);
         5124  +  sqlite3Fts5IndexCloseReader(p);
  5119   5125     fts5IndexDiscardData(p);
  5120   5126     fts5StructureInvalidate(p);
         5127  +  p->pConfig->iCookie = -1;
  5121   5128     /* assert( p->rc==SQLITE_OK ); */
  5122   5129     return SQLITE_OK;
  5123   5130   }
  5124   5131   
  5125   5132   /*
  5126   5133   ** The %_data table is completely empty when this function is called. This
  5127   5134   ** function populates it with the initial structure objects for each index,
................................................................................
  5348   5355           if( pSeg->pLeaf ) pRet->xSetOutputs(pRet, pSeg);
  5349   5356         }
  5350   5357       }
  5351   5358   
  5352   5359       if( p->rc ){
  5353   5360         sqlite3Fts5IterClose(&pRet->base);
  5354   5361         pRet = 0;
  5355         -      fts5CloseReader(p);
         5362  +      sqlite3Fts5IndexCloseReader(p);
  5356   5363       }
  5357   5364   
  5358   5365       *ppIter = &pRet->base;
  5359   5366       sqlite3Fts5BufferFree(&buf);
  5360   5367     }
  5361   5368     return fts5IndexReturn(p);
  5362   5369   }
................................................................................
  5421   5428   ** Close an iterator opened by an earlier call to sqlite3Fts5IndexQuery().
  5422   5429   */
  5423   5430   void sqlite3Fts5IterClose(Fts5IndexIter *pIndexIter){
  5424   5431     if( pIndexIter ){
  5425   5432       Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
  5426   5433       Fts5Index *pIndex = pIter->pIndex;
  5427   5434       fts5MultiIterFree(pIter);
  5428         -    fts5CloseReader(pIndex);
         5435  +    sqlite3Fts5IndexCloseReader(pIndex);
  5429   5436     }
  5430   5437   }
  5431   5438   
  5432   5439   /*
  5433   5440   ** Read and decode the "averages" record from the database. 
  5434   5441   **
  5435   5442   ** Parameter anSize must point to an array of size nCol, where nCol is
................................................................................
  5463   5470     assert( p->rc==SQLITE_OK );
  5464   5471     fts5DataWrite(p, FTS5_AVERAGES_ROWID, pData, nData);
  5465   5472     return fts5IndexReturn(p);
  5466   5473   }
  5467   5474   
  5468   5475   /*
  5469   5476   ** Return the total number of blocks this module has read from the %_data
  5470         -** table since it was created.
         5477  +** table (since it was created by sqlite3Fts5IndexOpen).
  5471   5478   */
  5472   5479   int sqlite3Fts5IndexReads(Fts5Index *p){
  5473   5480     return p->nRead;
  5474   5481   }
  5475   5482   
  5476   5483   /*
  5477         -** Set the 32-bit cookie value stored at the start of all structure 
  5478         -** records to the value passed as the second argument.
  5479         -**
  5480         -** Return SQLITE_OK if successful, or an SQLite error code if an error
  5481         -** occurs.
         5484  +** Increment the value of the configuration cookie stored as the first 
         5485  +** 32-bits of the structure record in the database. This is done after
         5486  +** modifying the contents of the %_config table.
  5482   5487   */
  5483         -int sqlite3Fts5IndexSetCookie(Fts5Index *p, int iNew){
  5484         -  int rc;                              /* Return code */
  5485         -  Fts5Config *pConfig = p->pConfig;    /* Configuration object */
  5486         -  u8 aCookie[4];                       /* Binary representation of iNew */
  5487         -  sqlite3_blob *pBlob = 0;
  5488         -
  5489         -  assert( p->rc==SQLITE_OK );
  5490         -  sqlite3Fts5Put32(aCookie, iNew);
  5491         -
  5492         -  rc = sqlite3_blob_open(pConfig->db, pConfig->zDb, p->zDataTbl, 
  5493         -      "block", FTS5_STRUCTURE_ROWID, 1, &pBlob
  5494         -  );
  5495         -  if( rc==SQLITE_OK ){
  5496         -    sqlite3_blob_write(pBlob, aCookie, 4, 0);
  5497         -    rc = sqlite3_blob_close(pBlob);
  5498         -  }
  5499         -
  5500         -  return rc;
  5501         -}
  5502         -
  5503         -int sqlite3Fts5IndexLoadConfig(Fts5Index *p){
         5488  +int sqlite3Fts5IndexIncrCookie(Fts5Index *p){
  5504   5489     Fts5Structure *pStruct;
  5505   5490     pStruct = fts5StructureRead(p);
         5491  +  p->pConfig->iCookie++;
         5492  +  fts5StructureWrite(p, pStruct);
  5506   5493     fts5StructureRelease(pStruct);
  5507   5494     return fts5IndexReturn(p);
  5508   5495   }
         5496  +
         5497  +/*
         5498  +** Ensure the contents of the %_config table have been loaded into memory.
         5499  +*/
         5500  +int sqlite3Fts5IndexLoadConfig(Fts5Index *p){
         5501  +  fts5StructureCache(p);
         5502  +  return fts5IndexReturn(p);
         5503  +}
         5504  +
         5505  +int sqlite3Fts5IndexNewTrans(Fts5Index *p){
         5506  +  assert( p->pStruct==0 || p->iStructVersion!=0 );
         5507  +  if( p->pConfig->iCookie<0 || fts5IndexDataVersion(p)!=p->iStructVersion ){
         5508  +    fts5StructureInvalidate(p);
         5509  +  }
         5510  +  return fts5IndexReturn(p);
         5511  +}
         5512  +
  5509   5513   
  5510   5514   
  5511   5515   /*************************************************************************
  5512   5516   **************************************************************************
  5513   5517   ** Below this point is the implementation of the integrity-check 
  5514   5518   ** functionality.
  5515   5519   */
................................................................................
  6447   6451       rc = sqlite3_create_function(
  6448   6452           db, "fts5_rowid", -1, SQLITE_UTF8, 0, fts5RowidFunction, 0, 0
  6449   6453       );
  6450   6454     }
  6451   6455     return rc;
  6452   6456   }
  6453   6457   
  6454         -
  6455         -int sqlite3Fts5IndexReset(Fts5Index *p){
  6456         -  assert( p->pStruct==0 || p->iStructVersion!=0 );
  6457         -  if( fts5IndexDataVersion(p)!=p->iStructVersion ){
  6458         -    fts5StructureInvalidate(p);
  6459         -  }
  6460         -  return fts5IndexReturn(p);
  6461         -}

Changes to ext/fts5/fts5_main.c.

   598    598   }
   599    599   
   600    600   static int fts5NewTransaction(Fts5Table *pTab){
   601    601     Fts5Cursor *pCsr;
   602    602     for(pCsr=pTab->pGlobal->pCsr; pCsr; pCsr=pCsr->pNext){
   603    603       if( pCsr->base.pVtab==(sqlite3_vtab*)pTab ) return SQLITE_OK;
   604    604     }
   605         -  return sqlite3Fts5StorageReset(pTab->pStorage);
          605  +  return sqlite3Fts5IndexNewTrans(pTab->pIndex);
   606    606   }
   607    607   
   608    608   /*
   609    609   ** Implementation of xOpen method.
   610    610   */
   611    611   static int fts5OpenMethod(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCsr){
   612    612     Fts5Table *pTab = (Fts5Table*)pVTab;
   613    613     Fts5Config *pConfig = pTab->pConfig;
   614    614     Fts5Cursor *pCsr = 0;           /* New cursor object */
   615    615     int nByte;                      /* Bytes of space to allocate */
   616    616     int rc;                         /* Return code */
   617    617   
   618    618     rc = fts5NewTransaction(pTab);
          619  +  if( rc==SQLITE_OK ){
          620  +    rc = sqlite3Fts5IndexLoadConfig(pTab->pIndex);
          621  +  }
   619    622     if( rc==SQLITE_OK ){
   620    623       nByte = sizeof(Fts5Cursor) + pConfig->nCol * sizeof(int);
   621    624       pCsr = (Fts5Cursor*)sqlite3_malloc(nByte);
   622    625       if( pCsr ){
   623    626         Fts5Global *pGlobal = pTab->pGlobal;
   624    627         memset(pCsr, 0, nByte);
   625    628         pCsr->aColumnSize = (int*)&pCsr[1];
................................................................................
   626    629         pCsr->pNext = pGlobal->pCsr;
   627    630         pGlobal->pCsr = pCsr;
   628    631         pCsr->iCsrId = ++pGlobal->iNextId;
   629    632       }else{
   630    633         rc = SQLITE_NOMEM;
   631    634       }
   632    635     }
          636  +  if( rc!=SQLITE_OK ){
          637  +    sqlite3Fts5IndexCloseReader(pTab->pIndex);
          638  +  }
   633    639     *ppCsr = (sqlite3_vtab_cursor*)pCsr;
   634    640     return rc;
   635    641   }
   636    642   
   637    643   static int fts5StmtType(Fts5Cursor *pCsr){
   638    644     if( pCsr->ePlan==FTS5_PLAN_SCAN ){
   639    645       return (pCsr->bDesc) ? FTS5_STMT_SCAN_DESC : FTS5_STMT_SCAN_ASC;
................................................................................
   706    712   
   707    713       fts5FreeCursorComponents(pCsr);
   708    714       /* Remove the cursor from the Fts5Global.pCsr list */
   709    715       for(pp=&pTab->pGlobal->pCsr; (*pp)!=pCsr; pp=&(*pp)->pNext);
   710    716       *pp = pCsr->pNext;
   711    717   
   712    718       sqlite3_free(pCsr);
          719  +    sqlite3Fts5IndexCloseReader(pTab->pIndex);
   713    720     }
   714    721     return SQLITE_OK;
   715    722   }
   716    723   
   717    724   static int fts5SorterNext(Fts5Cursor *pCsr){
   718    725     Fts5Sorter *pSorter = pCsr->pSorter;
   719    726     int rc;
................................................................................
  1585   1592     return rc;
  1586   1593   }
  1587   1594   
  1588   1595   /*
  1589   1596   ** Implementation of xBegin() method. 
  1590   1597   */
  1591   1598   static int fts5BeginMethod(sqlite3_vtab *pVtab){
  1592         -  fts5CheckTransactionState((Fts5Table*)pVtab, FTS5_BEGIN, 0);
  1593         -  fts5NewTransaction((Fts5Table*)pVtab);
  1594         -  return SQLITE_OK;
         1599  +  Fts5Table *pTab = (Fts5Table*)pVtab;
         1600  +  int rc;
         1601  +  rc = fts5NewTransaction(pTab);
         1602  +  if( rc!=SQLITE_OK ){
         1603  +    sqlite3Fts5IndexCloseReader(pTab->pIndex);
         1604  +  }
         1605  +#ifdef SQLITE_DEBUG
         1606  +  if( rc==SQLITE_OK ) fts5CheckTransactionState(pTab, FTS5_BEGIN, 0);
         1607  +#endif
         1608  +  return rc;
  1595   1609   }
  1596   1610   
  1597   1611   /*
  1598   1612   ** Implementation of xCommit() method. This is a no-op. The contents of
  1599   1613   ** the pending-terms hash-table have already been flushed into the database
  1600   1614   ** by fts5SyncMethod().
  1601   1615   */

Changes to ext/fts5/fts5_storage.c.

   637    637     return sqlite3Fts5IndexOptimize(p->pIndex);
   638    638   }
   639    639   
   640    640   int sqlite3Fts5StorageMerge(Fts5Storage *p, int nMerge){
   641    641     return sqlite3Fts5IndexMerge(p->pIndex, nMerge);
   642    642   }
   643    643   
   644         -int sqlite3Fts5StorageReset(Fts5Storage *p){
   645         -  return sqlite3Fts5IndexReset(p->pIndex);
   646         -}
   647         -
   648    644   /*
   649    645   ** Allocate a new rowid. This is used for "external content" tables when
   650    646   ** a NULL value is inserted into the rowid column. The new rowid is allocated
   651    647   ** by inserting a dummy row into the %_docsize table. The dummy will be
   652    648   ** overwritten later.
   653    649   **
   654    650   ** If the %_docsize table does not exist, SQLITE_MISMATCH is returned. In
................................................................................
  1116   1112       }else{
  1117   1113         sqlite3_bind_int(pReplace, 2, iVal);
  1118   1114       }
  1119   1115       sqlite3_step(pReplace);
  1120   1116       rc = sqlite3_reset(pReplace);
  1121   1117     }
  1122   1118     if( rc==SQLITE_OK && pVal ){
  1123         -    int iNew = p->pConfig->iCookie + 1;
  1124         -    rc = sqlite3Fts5IndexSetCookie(p->pIndex, iNew);
  1125         -    if( rc==SQLITE_OK ){
  1126         -      p->pConfig->iCookie = iNew;
  1127         -    }
         1119  +    rc = sqlite3Fts5IndexIncrCookie(p->pIndex);
  1128   1120     }
  1129   1121     return rc;
  1130   1122   }

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

    82     82   }
    83     83   
    84     84   set ::res [db eval {SELECT rowid, x1 FROM x1 WHERE x1 MATCH '*reads'}]
    85     85   
    86     86   do_faultsim_test 4 -faults oom-* -body {
    87     87     db eval {SELECT rowid, x, x1 FROM x1 WHERE x1 MATCH '*reads'}
    88     88   } -test {
    89         -  faultsim_test_result {0 {0 {} 3}}
           89  +  faultsim_test_result {0 {0 {} 4}}
    90     90   }
    91     91   
    92     92   #-------------------------------------------------------------------------
    93     93   # An OOM within a query that uses a custom rank function.
    94     94   #
    95     95   reset_db
    96     96   do_execsql_test 5.0 {

Added ext/fts5/test/fts5faultC.test.

            1  +# 2016 March 26
            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 is focused on OOM errors.
           13  +#
           14  +
           15  +source [file join [file dirname [info script]] fts5_common.tcl]
           16  +source $testdir/malloc_common.tcl
           17  +set testprefix fts5faultC
           18  +return_if_no_fts5
           19  +
           20  +#--------------------------------------------------------------------------
           21  +# Test that if an OOM error occurs while trying to set a configuration
           22  +# option, the in-memory and on-disk configurations are not left in an 
           23  +# inconsistent state.
           24  +#
           25  +
           26  +
           27  +
           28  +proc posrowid {cmd} { $cmd xRowid }
           29  +proc negrowid {cmd} { expr -1 * [$cmd xRowid] }
           30  +
           31  +sqlite3_fts5_create_function db posrowid posrowid
           32  +sqlite3_fts5_create_function db negrowid negrowid
           33  +
           34  +do_execsql_test 1.0.0 {
           35  +  CREATE VIRTUAL TABLE t1 USING fts5(x);
           36  +  INSERT INTO t1 VALUES('a b c');
           37  +  INSERT INTO t1 VALUES('d a d');
           38  +  INSERT INTO t1 VALUES('c b a');
           39  +}
           40  +do_execsql_test 1.0.1 {
           41  +  INSERT INTO t1(t1, rank) VALUES('rank', 'posrowid()');
           42  +  SELECT rowid FROM t1('a') ORDER BY rank;
           43  +} {1 2 3}
           44  +do_execsql_test 1.0.2 {
           45  +  INSERT INTO t1(t1, rank) VALUES('rank', 'negrowid()');
           46  +  SELECT rowid FROM t1('a') ORDER BY rank;
           47  +} {3 2 1}
           48  +
           49  +faultsim_save_and_close 
           50  +do_faultsim_test 1.1 -faults oom-* -prep {
           51  +  faultsim_restore_and_reopen
           52  +  sqlite3_fts5_create_function db posrowid posrowid
           53  +  sqlite3_fts5_create_function db negrowid negrowid
           54  +  execsql { SELECT * FROM t1('*reads') }
           55  +} -body {
           56  +  execsql { INSERT INTO t1(t1, rank) VALUES('rank', 'posrowid()') }
           57  +} -test {
           58  +
           59  +  faultsim_test_result [list 0 {}]
           60  +  sqlite3 db2 test.db
           61  +  set ex [db2 one { SELECT v FROM t1_config WHERE k='rank' }]
           62  +  switch -- $ex {
           63  +    "posrowid()" { set ex {1 2 3} }
           64  +    "negrowid()" { set ex {3 2 1} }
           65  +    default { error 1 }
           66  +  }
           67  +  
           68  +  set res [db eval { SELECT rowid FROM t1('a') ORDER BY rank }]
           69  +  if {$res != $ex} {
           70  +    error "2: expected {$ex} got {$res}"
           71  +  }
           72  +  db2 close
           73  +}
           74  +
           75  +
           76  +
           77  +
           78  +finish_test
           79  +

Changes to ext/fts5/test/fts5simple.test.

   346    346   
   347    347     WITH ii(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM ii WHERE i<10 )
   348    348     INSERT INTO x1 SELECT rnddoc(5) FROM ii;
   349    349   }
   350    350   
   351    351   do_execsql_test 14.4 {
   352    352     SELECT rowid, x, x1 FROM x1 WHERE x1 MATCH '*reads'
   353         -} {0 {} 3}
          353  +} {0 {} 4}
   354    354   
   355    355   #-------------------------------------------------------------------------
   356    356   reset_db
   357    357   do_execsql_test 15.0 {
   358    358     CREATE VIRTUAL TABLE x2 USING fts5(x, prefix=1);
   359    359     INSERT INTO x2 VALUES('ab');
   360    360   }