]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Fix various problems in fts5 revealed by fault-injection tests.
authordan <dan@noemail.net>
Thu, 18 Dec 2014 18:25:48 +0000 (18:25 +0000)
committerdan <dan@noemail.net>
Thu, 18 Dec 2014 18:25:48 +0000 (18:25 +0000)
FossilOrigin-Name: e358c3de5c916f2c851ab9324ceaae4e4e7a0fbd

ext/fts5/fts5.c
ext/fts5/fts5_config.c
ext/fts5/fts5_expr.c
ext/fts5/fts5_index.c
manifest
manifest.uuid
src/vtab.c
test/fts5fault1.test
test/malloc_common.tcl

index e80715a4c7447c5c735f52c895526785b874c761..4c6e98b86e334e1922067023fcda1310a77e95f8 100644 (file)
@@ -223,7 +223,7 @@ static void fts5CheckTransactionState(Fts5Table *p, int op, int iSavepoint){
       break;
 
     case FTS5_ROLLBACK:
-      assert( p->ts.eState==1 || p->ts.eState==2 );
+      assert( p->ts.eState==1 || p->ts.eState==2 || p->ts.eState==0 );
       p->ts.eState = 0;
       break;
 
index 9ea78143c5e2b238019a71fb9f95335318ed4478..54c7a57f28b0b4c956f5c161d5c0a33469a429b2 100644 (file)
@@ -74,14 +74,17 @@ static int fts5ConfigParseSpecial(
   char **pzErr                    /* OUT: Error message */
 ){
   if( sqlite3_stricmp(zCmd, "prefix")==0 ){
+    const int nByte = sizeof(int) * FTS5_MAX_PREFIX_INDEXES;
+    int rc = SQLITE_OK;
     char *p;
     if( pConfig->aPrefix ){
       *pzErr = sqlite3_mprintf("multiple prefix=... directives");
-      return SQLITE_ERROR;
+      rc = SQLITE_ERROR;
+    }else{
+      pConfig->aPrefix = sqlite3Fts5MallocZero(&rc, nByte);
     }
-    pConfig->aPrefix = sqlite3_malloc(sizeof(int) * FTS5_MAX_PREFIX_INDEXES);
     p = zArg;
-    while( p[0] ){
+    while( rc==SQLITE_OK && p[0] ){
       int nPre = 0;
       while( p[0]==' ' ) p++;
       while( p[0]>='0' && p[0]<='9' && nPre<1000 ){
@@ -93,16 +96,16 @@ static int fts5ConfigParseSpecial(
         p++;
       }else if( p[0] ){
         *pzErr = sqlite3_mprintf("malformed prefix=... directive");
-        return SQLITE_ERROR;
+        rc = SQLITE_ERROR;
       }
-      if( nPre==0 || nPre>=1000 ){
+      if( rc==SQLITE_OK && (nPre==0 || nPre>=1000) ){
         *pzErr = sqlite3_mprintf("prefix length out of range: %d", nPre);
-        return SQLITE_ERROR;
+        rc = SQLITE_ERROR;
       }
       pConfig->aPrefix[pConfig->nPrefix] = nPre;
       pConfig->nPrefix++;
     }
-    return SQLITE_OK;
+    return rc;
   }
 
   *pzErr = sqlite3_mprintf("unrecognized directive: \"%s\"", zCmd);
@@ -191,7 +194,7 @@ int sqlite3Fts5ConfigParse(
           }
 
           /* If it is not a special directive, it must be a column name. In
-           ** this case, check that it is not the reserved column name "rank". */
+          ** this case, check that it is not the reserved column name "rank". */
           if( zDup ){
             sqlite3Fts5Dequote(zDup);
             pRet->azCol[pRet->nCol++] = zDup;
index 5c95eda7e457774bfbfe726a03c899f03107482f..830af586b3f34039acf03f3c90bf093510a2311b 100644 (file)
@@ -213,6 +213,7 @@ int sqlite3Fts5ExprNew(
     *ppNew = pNew = sqlite3_malloc(sizeof(Fts5Expr));
     if( pNew==0 ){
       sParse.rc = SQLITE_NOMEM;
+      sqlite3Fts5ParseNodeFree(sParse.pExpr);
     }else{
       pNew->pRoot = sParse.pExpr;
       pNew->pIndex = 0;
@@ -771,7 +772,8 @@ static int fts5ExprNearInitAll(
           (pExpr->bAsc ? FTS5INDEX_QUERY_ASC : 0),
           &pTerm->pIter
       );
-      if( pTerm->pIter && sqlite3Fts5IterEof(pTerm->pIter) ){
+      assert( rc==SQLITE_OK || pTerm->pIter==0 );
+      if( pTerm->pIter==0 || sqlite3Fts5IterEof(pTerm->pIter) ){
         pNode->bEof = 1;
         break;
       }
@@ -1204,24 +1206,29 @@ Fts5ExprPhrase *sqlite3Fts5ParseTerm(
   int rc;                         /* Tokenize return code */
   char *z = 0;
 
+  memset(&sCtx, 0, sizeof(TokenCtx));
+  sCtx.pPhrase = pPhrase;
+
   if( pPhrase==0 ){
     if( (pParse->nPhrase % 8)==0 ){
       int nByte = sizeof(Fts5ExprPhrase*) * (pParse->nPhrase + 8);
       Fts5ExprPhrase **apNew;
       apNew = (Fts5ExprPhrase**)sqlite3_realloc(pParse->apPhrase, nByte);
-      if( apNew==0 ) return 0;
+      if( apNew==0 ){
+        pParse->rc = SQLITE_NOMEM;
+        fts5ExprPhraseFree(pPhrase);
+        return 0;
+      }
       pParse->apPhrase = apNew;
     }
     pParse->nPhrase++;
   }
 
-  pParse->rc = fts5ParseStringFromToken(pToken, &z);
-  if( z==0 ) return 0;
-  sqlite3Fts5Dequote(z);
-
-  memset(&sCtx, 0, sizeof(TokenCtx));
-  sCtx.pPhrase = pPhrase;
-  rc = sqlite3Fts5Tokenize(pConfig, z, strlen(z), &sCtx, fts5ParseTokenize);
+  rc = fts5ParseStringFromToken(pToken, &z);
+  if( rc==SQLITE_OK ){
+    sqlite3Fts5Dequote(z);
+    rc = sqlite3Fts5Tokenize(pConfig, z, strlen(z), &sCtx, fts5ParseTokenize);
+  }
   if( rc ){
     pParse->rc = rc;
     fts5ExprPhraseFree(sCtx.pPhrase);
index 7d0d01afdf5081a5b42e79ed1e36ba7dacadb0b5..95b2cdb2f794162923d50008263520bbd4572761 100644 (file)
@@ -602,13 +602,14 @@ static u16 fts5GetU16(const u8 *aIn){
 ** the Fts5Index handle passed as the first argument.
 */
 static void *fts5IdxMalloc(Fts5Index *p, int nByte){
-  void *pRet;
-  assert( p->rc==SQLITE_OK );
-  pRet = sqlite3_malloc(nByte);
-  if( pRet==0 ){
-    p->rc = SQLITE_NOMEM;
-  }else{
-    memset(pRet, 0, nByte);
+  void *pRet = 0;
+  if( p->rc==SQLITE_OK ){
+    pRet = sqlite3_malloc(nByte);
+    if( pRet==0 ){
+      p->rc = SQLITE_NOMEM;
+    }else{
+      memset(pRet, 0, nByte);
+    }
   }
   return pRet;
 }
@@ -662,8 +663,9 @@ static int fts5BufferCompare(Fts5Buffer *pLeft, Fts5Buffer *pRight){
 */
 static void fts5CloseReader(Fts5Index *p){
   if( p->pReader ){
-    sqlite3_blob_close(p->pReader);
+    sqlite3_blob *pReader = p->pReader;
     p->pReader = 0;
+    sqlite3_blob_close(pReader);
   }
 }
 
@@ -707,9 +709,10 @@ sqlite3_free(buf.p);
       int nByte = sqlite3_blob_bytes(p->pReader);
       if( pBuf ){
         fts5BufferZero(pBuf);
-        fts5BufferGrow(&rc, pBuf, nByte);
-        rc = sqlite3_blob_read(p->pReader, pBuf->p, nByte, 0);
-        if( rc==SQLITE_OK ) pBuf->n = nByte;
+        if( SQLITE_OK==fts5BufferGrow(&rc, pBuf, nByte) ){
+          rc = sqlite3_blob_read(p->pReader, pBuf->p, nByte, 0);
+          if( rc==SQLITE_OK ) pBuf->n = nByte;
+        }
       }else{
         pRet = (Fts5Data*)fts5IdxMalloc(p, sizeof(Fts5Data) + nByte);
         if( !pRet ) return 0;
@@ -854,6 +857,20 @@ static void fts5DataRemoveSegment(Fts5Index *p, int iIdx, int iSegid){
   fts5DataDelete(p, iFirst, iLast);
 }
 
+/*
+** Release a reference to an Fts5Structure object returned by an earlier 
+** call to fts5StructureRead() or fts5StructureDecode().
+*/
+static void fts5StructureRelease(Fts5Structure *pStruct){
+  if( pStruct ){
+    int i;
+    for(i=0; i<pStruct->nLevel; i++){
+      sqlite3_free(pStruct->aLevel[i].aSeg);
+    }
+    sqlite3_free(pStruct);
+  }
+}
+
 /*
 ** Deserialize and return the structure record currently stored in serialized
 ** form within buffer pData/nData.
@@ -918,6 +935,9 @@ static int fts5StructureDecode(
           i += getVarint32(&pData[i], pLvl->aSeg[iSeg].pgnoFirst);
           i += getVarint32(&pData[i], pLvl->aSeg[iSeg].pgnoLast);
         }
+      }else{
+        fts5StructureRelease(pRet);
+        pRet = 0;
       }
     }
   }
@@ -1009,19 +1029,11 @@ static Fts5Structure *fts5StructureRead(Fts5Index *p, int iIdx){
   }
 
   fts5DataRelease(pData);
-  return pRet;
-}
-
-/*
-** Release a reference to an Fts5Structure object returned by an earlier 
-** call to fts5StructureRead() or fts5StructureDecode().
-*/
-static void fts5StructureRelease(Fts5Structure *pStruct){
-  int i;
-  for(i=0; i<pStruct->nLevel; i++){
-    sqlite3_free(pStruct->aLevel[i].aSeg);
+  if( p->rc!=SQLITE_OK ){
+    fts5StructureRelease(pRet);
+    pRet = 0;
   }
-  sqlite3_free(pStruct);
+  return pRet;
 }
 
 /*
@@ -1045,40 +1057,42 @@ static int fts5StructureCountSegments(Fts5Structure *pStruct){
 ** error has already occurred, this function is a no-op.
 */
 static void fts5StructureWrite(Fts5Index *p, int iIdx, Fts5Structure *pStruct){
-  int nSegment;                   /* Total number of segments */
-  Fts5Buffer buf;                 /* Buffer to serialize record into */
-  int iLvl;                       /* Used to iterate through levels */
-  int iCookie;                    /* Cookie value to store */
+  if( p->rc==SQLITE_OK ){
+    int nSegment;                 /* Total number of segments */
+    Fts5Buffer buf;               /* Buffer to serialize record into */
+    int iLvl;                     /* Used to iterate through levels */
+    int iCookie;                  /* Cookie value to store */
 
-  nSegment = fts5StructureCountSegments(pStruct);
-  memset(&buf, 0, sizeof(Fts5Buffer));
+    nSegment = fts5StructureCountSegments(pStruct);
+    memset(&buf, 0, sizeof(Fts5Buffer));
 
-  /* Append the current configuration cookie */
-  iCookie = p->pConfig->iCookie;
-  if( iCookie<0 ) iCookie = 0;
-  fts5BufferAppend32(&p->rc, &buf, iCookie);
+    /* Append the current configuration cookie */
+    iCookie = p->pConfig->iCookie;
+    if( iCookie<0 ) iCookie = 0;
+    fts5BufferAppend32(&p->rc, &buf, iCookie);
 
-  fts5BufferAppendVarint(&p->rc, &buf, pStruct->nLevel);
-  fts5BufferAppendVarint(&p->rc, &buf, nSegment);
-  fts5BufferAppendVarint(&p->rc, &buf, (i64)pStruct->nWriteCounter);
+    fts5BufferAppendVarint(&p->rc, &buf, pStruct->nLevel);
+    fts5BufferAppendVarint(&p->rc, &buf, nSegment);
+    fts5BufferAppendVarint(&p->rc, &buf, (i64)pStruct->nWriteCounter);
 
-  for(iLvl=0; iLvl<pStruct->nLevel; iLvl++){
-    int iSeg;                     /* Used to iterate through segments */
-    Fts5StructureLevel *pLvl = &pStruct->aLevel[iLvl];
-    fts5BufferAppendVarint(&p->rc, &buf, pLvl->nMerge);
-    fts5BufferAppendVarint(&p->rc, &buf, pLvl->nSeg);
-    assert( pLvl->nMerge<=pLvl->nSeg );
-
-    for(iSeg=0; iSeg<pLvl->nSeg; iSeg++){
-      fts5BufferAppendVarint(&p->rc, &buf, pLvl->aSeg[iSeg].iSegid);
-      fts5BufferAppendVarint(&p->rc, &buf, pLvl->aSeg[iSeg].nHeight);
-      fts5BufferAppendVarint(&p->rc, &buf, pLvl->aSeg[iSeg].pgnoFirst);
-      fts5BufferAppendVarint(&p->rc, &buf, pLvl->aSeg[iSeg].pgnoLast);
+    for(iLvl=0; iLvl<pStruct->nLevel; iLvl++){
+      int iSeg;                     /* Used to iterate through segments */
+      Fts5StructureLevel *pLvl = &pStruct->aLevel[iLvl];
+      fts5BufferAppendVarint(&p->rc, &buf, pLvl->nMerge);
+      fts5BufferAppendVarint(&p->rc, &buf, pLvl->nSeg);
+      assert( pLvl->nMerge<=pLvl->nSeg );
+
+      for(iSeg=0; iSeg<pLvl->nSeg; iSeg++){
+        fts5BufferAppendVarint(&p->rc, &buf, pLvl->aSeg[iSeg].iSegid);
+        fts5BufferAppendVarint(&p->rc, &buf, pLvl->aSeg[iSeg].nHeight);
+        fts5BufferAppendVarint(&p->rc, &buf, pLvl->aSeg[iSeg].pgnoFirst);
+        fts5BufferAppendVarint(&p->rc, &buf, pLvl->aSeg[iSeg].pgnoLast);
+      }
     }
-  }
 
-  fts5DataWrite(p, FTS5_STRUCTURE_ROWID(iIdx), buf.p, buf.n);
-  fts5BufferFree(&buf);
+    fts5DataWrite(p, FTS5_STRUCTURE_ROWID(iIdx), buf.p, buf.n);
+    fts5BufferFree(&buf);
+  }
 }
 
 #if 0
@@ -1864,7 +1878,7 @@ static void fts5SegIterSeekInit(
       res = fts5BufferCompareBlob(&pIter->term, pTerm, nTerm);
       if( res>=0 ) break;
       fts5SegIterNext(p, pIter);
-    }while( pIter->pLeaf );
+    }while( pIter->pLeaf && p->rc==SQLITE_OK );
 
     if( bGe==0 && res ){
       /* Set iterator to point to EOF */
@@ -1873,7 +1887,7 @@ static void fts5SegIterSeekInit(
     }
   }
 
-  if( bGe==0 ){
+  if( p->rc==SQLITE_OK && bGe==0 ){
     pIter->flags |= FTS5_SEGITER_ONETERM;
     if( pIter->pLeaf ){
       if( flags & FTS5INDEX_QUERY_ASC ){
@@ -2422,7 +2436,7 @@ static void fts5IndexDiscardData(Fts5Index *p){
     Fts5Config *pConfig = p->pConfig;
     int i;
     for(i=0; i<=pConfig->nPrefix; i++){
-      sqlite3Fts5HashClear(p->apHash[i]);
+      if( p->apHash[i] ) sqlite3Fts5HashClear(p->apHash[i]);
     }
     p->nPendingData = 0;
   }
@@ -2609,8 +2623,8 @@ static void fts5WriteAppendTerm(
   int nPrefix;                    /* Bytes of prefix compression for term */
   Fts5PageWriter *pPage = &pWriter->aWriter[0];
 
-  assert( pPage->buf.n==0 || pPage->buf.n>4 );
-  if( pPage->buf.n==0 ){
+  assert( pPage==0 || pPage->buf.n==0 || pPage->buf.n>4 );
+  if( pPage && pPage->buf.n==0 ){
     /* Zero the first term and first docid fields */
     static const u8 zero[] = { 0x00, 0x00, 0x00, 0x00 };
     fts5BufferAppendBlob(&p->rc, &pPage->buf, 4, zero);
@@ -2660,30 +2674,32 @@ static void fts5WriteAppendRowid(
   Fts5SegWriter *pWriter,
   i64 iRowid
 ){
-  Fts5PageWriter *pPage = &pWriter->aWriter[0];
-
-  /* If this is to be the first docid written to the page, set the 
-  ** docid-pointer in the page-header. Also append a value to the dlidx
-  ** buffer, in case a doclist-index is required.  */
-  if( pWriter->bFirstRowidInPage ){
-    fts5PutU16(pPage->buf.p, pPage->buf.n);
-    fts5WriteDlidxAppend(p, pWriter, iRowid);
-  }
+  if( p->rc==SQLITE_OK ){
+    Fts5PageWriter *pPage = &pWriter->aWriter[0];
+
+    /* If this is to be the first docid written to the page, set the 
+    ** docid-pointer in the page-header. Also append a value to the dlidx
+    ** buffer, in case a doclist-index is required.  */
+    if( pWriter->bFirstRowidInPage ){
+      fts5PutU16(pPage->buf.p, pPage->buf.n);
+      fts5WriteDlidxAppend(p, pWriter, iRowid);
+    }
 
-  /* Write the docid. */
-  if( pWriter->bFirstRowidInDoclist || pWriter->bFirstRowidInPage ){
-    fts5BufferAppendVarint(&p->rc, &pPage->buf, iRowid);
-  }else{
-    assert( iRowid<pWriter->iPrevRowid );
-    fts5BufferAppendVarint(&p->rc, &pPage->buf, pWriter->iPrevRowid - iRowid);
-  }
-  pWriter->iPrevRowid = iRowid;
-  pWriter->bFirstRowidInDoclist = 0;
-  pWriter->bFirstRowidInPage = 0;
+    /* Write the docid. */
+    if( pWriter->bFirstRowidInDoclist || pWriter->bFirstRowidInPage ){
+      fts5BufferAppendVarint(&p->rc, &pPage->buf, iRowid);
+    }else{
+      assert( p->rc || iRowid<pWriter->iPrevRowid );
+      fts5BufferAppendVarint(&p->rc, &pPage->buf, pWriter->iPrevRowid - iRowid);
+    }
+    pWriter->iPrevRowid = iRowid;
+    pWriter->bFirstRowidInDoclist = 0;
+    pWriter->bFirstRowidInPage = 0;
 
-  if( pPage->buf.n>=p->pConfig->pgsz ){
-    fts5WriteFlushLeaf(p, pWriter);
-    pWriter->bFirstRowidInPage = 1;
+    if( pPage->buf.n>=p->pConfig->pgsz ){
+      fts5WriteFlushLeaf(p, pWriter);
+      pWriter->bFirstRowidInPage = 1;
+    }
   }
 }
 
@@ -2692,11 +2708,13 @@ static void fts5WriteAppendPoslistInt(
   Fts5SegWriter *pWriter,
   int iVal
 ){
-  Fts5PageWriter *pPage = &pWriter->aWriter[0];
-  fts5BufferAppendVarint(&p->rc, &pPage->buf, iVal);
-  if( pPage->buf.n>=p->pConfig->pgsz ){
-    fts5WriteFlushLeaf(p, pWriter);
-    pWriter->bFirstRowidInPage = 1;
+  if( p->rc==SQLITE_OK ){
+    Fts5PageWriter *pPage = &pWriter->aWriter[0];
+    fts5BufferAppendVarint(&p->rc, &pPage->buf, iVal);
+    if( pPage->buf.n>=p->pConfig->pgsz ){
+      fts5WriteFlushLeaf(p, pWriter);
+      pWriter->bFirstRowidInPage = 1;
+    }
   }
 }
 
@@ -2744,32 +2762,37 @@ static void fts5WriteFinish(
   int *pnLeaf                     /* OUT: Number of leaf pages in b-tree */
 ){
   int i;
-  *pnLeaf = pWriter->aWriter[0].pgno;
-  if( *pnLeaf==1 && pWriter->aWriter[0].buf.n==0 ){
-    *pnLeaf = 0;
-    *pnHeight = 0;
-  }else{
-    fts5WriteFlushLeaf(p, pWriter);
-    if( pWriter->nWriter==1 && pWriter->nEmpty>=FTS5_MIN_DLIDX_SIZE ){
-      fts5WriteBtreeGrow(p, pWriter);
-    }
-    if( pWriter->nWriter>1 ){
-      fts5WriteBtreeNEmpty(p, pWriter);
-    }
-    *pnHeight = pWriter->nWriter;
+  if( p->rc==SQLITE_OK ){
+    *pnLeaf = pWriter->aWriter[0].pgno;
+    if( *pnLeaf==1 && pWriter->aWriter[0].buf.n==0 ){
+      *pnLeaf = 0;
+      *pnHeight = 0;
+    }else{
+      fts5WriteFlushLeaf(p, pWriter);
+      if( pWriter->nWriter==1 && pWriter->nEmpty>=FTS5_MIN_DLIDX_SIZE ){
+        fts5WriteBtreeGrow(p, pWriter);
+      }
+      if( pWriter->nWriter>1 ){
+        fts5WriteBtreeNEmpty(p, pWriter);
+      }
+      *pnHeight = pWriter->nWriter;
 
-    for(i=1; i<pWriter->nWriter; i++){
-      Fts5PageWriter *pPg = &pWriter->aWriter[i];
-      fts5DataWrite(p, 
-          FTS5_SEGMENT_ROWID(pWriter->iIdx, pWriter->iSegid, i, pPg->pgno), 
-          pPg->buf.p, pPg->buf.n
-      );
+      for(i=1; i<pWriter->nWriter; i++){
+        Fts5PageWriter *pPg = &pWriter->aWriter[i];
+        fts5DataWrite(p, 
+            FTS5_SEGMENT_ROWID(pWriter->iIdx, pWriter->iSegid, i, pPg->pgno), 
+            pPg->buf.p, pPg->buf.n
+        );
+      }
     }
   }
   for(i=0; i<pWriter->nWriter; i++){
     Fts5PageWriter *pPg = &pWriter->aWriter[i];
-    fts5BufferFree(&pPg->term);
-    fts5BufferFree(&pPg->buf);
+    assert( pPg || p->rc!=SQLITE_OK );
+    if( pPg ){
+      fts5BufferFree(&pPg->term);
+      fts5BufferFree(&pPg->buf);
+    }
   }
   sqlite3_free(pWriter->aWriter);
   sqlite3Fts5BufferFree(&pWriter->dlidx);
@@ -3025,55 +3048,57 @@ static void fts5IndexWork(
   Fts5Structure **ppStruct,       /* IN/OUT: Current structure of index */
   int nLeaf                       /* Number of output leaves just written */
 ){
-  Fts5Structure *pStruct = *ppStruct;
-  i64 nWrite;                     /* Initial value of write-counter */
-  int nWork;                      /* Number of work-quanta to perform */
-  int nRem;                       /* Number of leaf pages left to write */
-
-  /* Update the write-counter. While doing so, set nWork. */
-  nWrite = pStruct->nWriteCounter;
-  nWork = ((nWrite + nLeaf) / p->nWorkUnit) - (nWrite / p->nWorkUnit);
-  pStruct->nWriteCounter += nLeaf;
-  nRem = p->nWorkUnit * nWork * pStruct->nLevel;
-
-  while( nRem>0 ){
-    int iLvl;                   /* To iterate through levels */
-    int iBestLvl = 0;           /* Level offering the most input segments */
-    int nBest = 0;              /* Number of input segments on best level */
-
-    /* Set iBestLvl to the level to read input segments from. */
-    assert( pStruct->nLevel>0 );
-    for(iLvl=0; iLvl<pStruct->nLevel; iLvl++){
-      Fts5StructureLevel *pLvl = &pStruct->aLevel[iLvl];
-      if( pLvl->nMerge ){
-        if( pLvl->nMerge>nBest ){
+  if( p->rc==SQLITE_OK ){
+    Fts5Structure *pStruct = *ppStruct;
+    i64 nWrite;                   /* Initial value of write-counter */
+    int nWork;                    /* Number of work-quanta to perform */
+    int nRem;                     /* Number of leaf pages left to write */
+
+    /* Update the write-counter. While doing so, set nWork. */
+    nWrite = pStruct->nWriteCounter;
+    nWork = ((nWrite + nLeaf) / p->nWorkUnit) - (nWrite / p->nWorkUnit);
+    pStruct->nWriteCounter += nLeaf;
+    nRem = p->nWorkUnit * nWork * pStruct->nLevel;
+
+    while( nRem>0 ){
+      int iLvl;                   /* To iterate through levels */
+      int iBestLvl = 0;           /* Level offering the most input segments */
+      int nBest = 0;              /* Number of input segments on best level */
+
+      /* Set iBestLvl to the level to read input segments from. */
+      assert( pStruct->nLevel>0 );
+      for(iLvl=0; iLvl<pStruct->nLevel; iLvl++){
+        Fts5StructureLevel *pLvl = &pStruct->aLevel[iLvl];
+        if( pLvl->nMerge ){
+          if( pLvl->nMerge>nBest ){
+            iBestLvl = iLvl;
+            nBest = pLvl->nMerge;
+          }
+          break;
+        }
+        if( pLvl->nSeg>nBest ){
+          nBest = pLvl->nSeg;
           iBestLvl = iLvl;
-          nBest = pLvl->nMerge;
         }
-        break;
       }
-      if( pLvl->nSeg>nBest ){
-        nBest = pLvl->nSeg;
-        iBestLvl = iLvl;
-      }
-    }
 
-    /* If nBest is still 0, then the index must be empty. */
+      /* If nBest is still 0, then the index must be empty. */
 #ifdef SQLITE_DEBUG
-    for(iLvl=0; nBest==0 && iLvl<pStruct->nLevel; iLvl++){
-      assert( pStruct->aLevel[iLvl].nSeg==0 );
-    }
+      for(iLvl=0; nBest==0 && iLvl<pStruct->nLevel; iLvl++){
+        assert( pStruct->aLevel[iLvl].nSeg==0 );
+      }
 #endif
 
-    if( nBest<p->pConfig->nAutomerge 
-     && pStruct->aLevel[iBestLvl].nMerge==0 
-    ){
-      break;
+      if( nBest<p->pConfig->nAutomerge 
+          && pStruct->aLevel[iBestLvl].nMerge==0 
+        ){
+        break;
+      }
+      fts5IndexMergeLevel(p, iIdx, &pStruct, iBestLvl, &nRem);
+      fts5StructurePromote(p, iBestLvl+1, pStruct);
+      assert( nRem==0 || p->rc==SQLITE_OK );
+      *ppStruct = pStruct;
     }
-    fts5IndexMergeLevel(p, iIdx, &pStruct, iBestLvl, &nRem);
-    fts5StructurePromote(p, iBestLvl+1, pStruct);
-    assert( nRem==0 || p->rc==SQLITE_OK );
-    *ppStruct = pStruct;
   }
 }
 
@@ -3123,17 +3148,17 @@ static int fts5FlushNewEntry(
   int nPoslist
 ){
   Fts5FlushCtx *p = (Fts5FlushCtx*)pCtx;
-  int rc = SQLITE_OK;
+  Fts5Index *pIdx = p->pIdx;
 
   /* Append the rowid itself */
-  fts5WriteAppendRowid(p->pIdx, &p->writer, iRowid);
+  fts5WriteAppendRowid(pIdx, &p->writer, iRowid);
 
   /* Append the size of the position list in bytes */
-  fts5WriteAppendPoslistInt(p->pIdx, &p->writer, nPoslist);
+  fts5WriteAppendPoslistInt(pIdx, &p->writer, nPoslist);
 
   /* And the poslist data */
-  fts5WriteAppendPoslistData(p->pIdx, &p->writer, aPoslist, nPoslist);
-  return rc;
+  fts5WriteAppendPoslistData(pIdx, &p->writer, aPoslist, nPoslist);
+  return pIdx->rc;
 }
 
 /*
@@ -3485,20 +3510,22 @@ static void fts5MultiIterPoslist(
   int bSz,
   Fts5Buffer *pBuf
 ){
-  Fts5ChunkIter iter;
-  Fts5SegIter *pSeg = &pMulti->aSeg[ pMulti->aFirst[1] ];
-  assert( fts5MultiIterEof(p, pMulti)==0 );
-  fts5ChunkIterInit(p, pSeg, &iter);
-  if( fts5ChunkIterEof(p, &iter)==0 ){
-    if( bSz ){
-      fts5BufferAppendVarint(&p->rc, pBuf, iter.nRem);
-    }
-    while( fts5ChunkIterEof(p, &iter)==0 ){
-      fts5BufferAppendBlob(&p->rc, pBuf, iter.n, iter.p);
-      fts5ChunkIterNext(p, &iter);
+  if( p->rc==SQLITE_OK ){
+    Fts5ChunkIter iter;
+    Fts5SegIter *pSeg = &pMulti->aSeg[ pMulti->aFirst[1] ];
+    assert( fts5MultiIterEof(p, pMulti)==0 );
+    fts5ChunkIterInit(p, pSeg, &iter);
+    if( fts5ChunkIterEof(p, &iter)==0 ){
+      if( bSz ){
+        fts5BufferAppendVarint(&p->rc, pBuf, iter.nRem);
+      }
+      while( fts5ChunkIterEof(p, &iter)==0 ){
+        fts5BufferAppendBlob(&p->rc, pBuf, iter.n, iter.p);
+        fts5ChunkIterNext(p, &iter);
+      }
     }
+    fts5ChunkIterRelease(&iter);
   }
-  fts5ChunkIterRelease(&iter);
 }
 
 static void fts5DoclistIterNext(Fts5DoclistIter *pIter){
index a2dd5734ff2e5e133b7d0d1b7ee79e50652aed8b..a7170b5cfd5601cbd80279b5c74531954a6b805a 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Begin\stesting\sfts5\sOOM\sand\sIO\serror\shandling.
-D 2014-12-03T17:27:35.105
+C Fix\svarious\sproblems\sin\sfts5\srevealed\sby\sfault-injection\stests.
+D 2014-12-18T18:25:48.377
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in b03432313a3aad96c706f8164fb9f5307eaf19f5
 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -104,15 +104,15 @@ F ext/fts3/unicode/CaseFolding.txt 8c678ca52ecc95e16bc7afc2dbf6fc9ffa05db8c
 F ext/fts3/unicode/UnicodeData.txt cd07314edb62d49fde34debdaf92fa2aa69011e7
 F ext/fts3/unicode/mkunicode.tcl dc6f268eb526710e2c6e496c372471d773d0c368
 F ext/fts5/extract_api_docs.tcl 6320db4a1d0722a4e2069e661381ad75e9889786
-F ext/fts5/fts5.c 1dae34f4a788b5760c52b914d6384d83ee027b35
+F ext/fts5/fts5.c d1c1722eb661da3e8e3a19909958b97beff7d243
 F ext/fts5/fts5.h 72fc1e9995b1ddc254a487b9528614a83bd3dfb6
 F ext/fts5/fts5Int.h 36054b1dfc4881a9b94f945b348ab6cc01c0c7a5
 F ext/fts5/fts5_aux.c 0e3e5fea6bf5772805afe14c95cb5f16e03e4b3f
 F ext/fts5/fts5_buffer.c 1bc5c762bb2e9b4a40b2e8a820a31b809e72eec1
-F ext/fts5/fts5_config.c 17986112dc76e7e39170e08df68f84180f66a9fe
-F ext/fts5/fts5_expr.c 5db50cd4ae9c3764d7daa8388bf406c0bad15039
+F ext/fts5/fts5_config.c 5caeb4e77680d635be25b899f97a29cf26fb45ce
+F ext/fts5/fts5_expr.c 27d3d2deebae277c34ae2bb3d501dd879c442ba5
 F ext/fts5/fts5_hash.c 63fa8379c5f2ac107d47c2b7d9ac04c95ef8a279
-F ext/fts5/fts5_index.c 9233b8b1f519e50d9ec139031032d9211dfcb541
+F ext/fts5/fts5_index.c 13b6d002e10840d8ec525ccd4a2bfc8831ea7a47
 F ext/fts5/fts5_storage.c bfeedb83b095a1018f4f531c3cc3f9099e9f9081
 F ext/fts5/fts5_tcl.c 5272224faf9be129679da5e19d788f0307afc375
 F ext/fts5/fts5_tokenize.c 8360c0d1ae0d4696f3cc13f7c67a2db6011cdc5b
@@ -306,7 +306,7 @@ F src/vdbeblob.c 9205ce9d3b064d9600f8418a897fc88b5687d9ac
 F src/vdbemem.c 6fc77594c60f6155404f3f8d71bf36d1fdeb4447
 F src/vdbesort.c 44441d73b08b3a638dcdb725afffb87c6574ad27
 F src/vdbetrace.c 6f52bc0c51e144b7efdcfb2a8f771167a8816767
-F src/vtab.c 21b932841e51ebd7d075e2d0ad1415dce8d2d5fd
+F src/vtab.c b05a86bed6ee2c7f79edb2cb57b951b220867caf
 F src/wal.c 264df50a1b33124130b23180ded2e2c5663c652a
 F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4
 F src/walker.c 11edb74d587bc87b33ca96a5173e3ec1b8389e45
@@ -612,7 +612,7 @@ F test/fts5aj.test bc3d91bd012c7ca175cdf266c2074920bb5fa5ba
 F test/fts5ak.test e55bb0f3fac1291d32bc9485a3ee55a7d76f4d5f
 F test/fts5al.test 61b067f3b0b61679ab164a8a855882dfd313988d
 F test/fts5ea.test afaf3497b43add578384dc1fd26b0342738abe87
-F test/fts5fault1.test 27cb71251f8f2cd710ce4bdc1f0c29fa5db83be7
+F test/fts5fault1.test 6fef96cf6eccd9b9fc9f4518cc15c4fa9740ef66
 F test/full.test 6b3c8fb43c6beab6b95438c1675374b95fab245d
 F test/func.test ae97561957aba6ca9e3a7b8a13aac41830d701ef
 F test/func2.test 772d66227e4e6684b86053302e2d74a2500e1e0f
@@ -722,7 +722,7 @@ F test/mallocH.test 79b65aed612c9b3ed2dcdaa727c85895fd1bfbdb
 F test/mallocI.test a88c2b9627c8506bf4703d8397420043a786cdb6
 F test/mallocJ.test b5d1839da331d96223e5f458856f8ffe1366f62e
 F test/mallocK.test d79968641d1b70d88f6c01bdb9a7eb4a55582cc9
-F test/malloc_common.tcl 58e54229c4132ef882a11fab6419ec4cd3073589
+F test/malloc_common.tcl dc07e2bbbfc3f8416f4cbddcc139d77eb360d9af
 F test/manydb.test 28385ae2087967aa05c38624cec7d96ec74feb3e
 F test/mem5.test c6460fba403c5703141348cd90de1c294188c68f
 F test/memdb.test fcb5297b321b562084fc79d64d5a12a1cd2b639b
@@ -1208,7 +1208,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
 F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
-P b5f5971283b9b2f60c16f9675099855af95012cd
-R cb30a6b5c5f7ea511cc1ede93a5d038a
+P 2037dba62fdd995ad15b642abe499a790f5ffe5c
+R 701ca9549e5d1bd70b92dde1949b5886
 U dan
-Z f7fa77a51653f0fa8e3497900e76f571
+Z d83a5deacda027d17d377616a8122fa0
index f14250e59b2251798eab5e66b1b3169c34417e72..47e70fa9388ca3da043b9619c7ea0e853149566d 100644 (file)
@@ -1 +1 @@
-2037dba62fdd995ad15b642abe499a790f5ffe5c
\ No newline at end of file
+e358c3de5c916f2c851ab9324ceaae4e4e7a0fbd
\ No newline at end of file
index ca0db214cc5ce651aa09147f9d6381b52b0af4fd..7b4867f9c0afb5c90a74e4dedde11fc367a88b22 100644 (file)
@@ -789,8 +789,10 @@ int sqlite3VtabCallDestroy(sqlite3 *db, int iDb, const char *zTab){
 static void callFinaliser(sqlite3 *db, int offset){
   int i;
   if( db->aVTrans ){
+    VTable **aVTrans = db->aVTrans;
+    db->aVTrans = 0;
     for(i=0; i<db->nVTrans; i++){
-      VTable *pVTab = db->aVTrans[i];
+      VTable *pVTab = aVTrans[i];
       sqlite3_vtab *p = pVTab->pVtab;
       if( p ){
         int (*x)(sqlite3_vtab *);
@@ -800,9 +802,8 @@ static void callFinaliser(sqlite3 *db, int offset){
       pVTab->iSavepoint = 0;
       sqlite3VtabUnlock(pVTab);
     }
-    sqlite3DbFree(db, db->aVTrans);
+    sqlite3DbFree(db, aVTrans);
     db->nVTrans = 0;
-    db->aVTrans = 0;
   }
 }
 
index 723ae3d22f9ab1f8ad6dfd8b485cd1ac0d208503..76434b3a80349033f088e1eb591d0e0f0aa2f3c7 100644 (file)
@@ -23,15 +23,89 @@ ifcapable !fts5 {
   return
 }
 
+# Simple tests:
+#
+#   1: CREATE VIRTUAL TABLE
+#   2: INSERT statement
+#   3: DELETE statement
+#   4: MATCH expressions
+#
+
+if 1 {
+
 faultsim_save_and_close
 do_faultsim_test 1 -prep {
   faultsim_restore_and_reopen
 } -body {
-  execsql { CREATE VIRTUAL TABLE t1 USING fts5(a) }
+  execsql { CREATE VIRTUAL TABLE t1 USING fts5(a, b, prefix='1, 2, 3') }
+} -test {
+  faultsim_test_result {0 {}} 
+}
+
+reset_db
+do_execsql_test 2.0 {
+  CREATE VIRTUAL TABLE t1 USING fts5(a, b, prefix='1, 2, 3');
+}
+faultsim_save_and_close
+do_faultsim_test 2 -prep {
+  faultsim_restore_and_reopen
+} -body {
+  execsql { 
+    INSERT INTO t1 VALUES('a b c', 'a bc def ghij klmno');
+  }
+} -test {
+  faultsim_test_result {0 {}} 
+}
+
+reset_db
+do_execsql_test 3.0 {
+  CREATE VIRTUAL TABLE t1 USING fts5(a, b, prefix='1, 2, 3');
+  INSERT INTO t1 VALUES('a b c', 'a bc def ghij klmno');
+}
+faultsim_save_and_close
+do_faultsim_test 3 -prep {
+  faultsim_restore_and_reopen
+} -body {
+  execsql { DELETE FROM t1 }
 } -test {
   faultsim_test_result {0 {}} 
 }
 
+}
+
+reset_db
+do_execsql_test 4.0 {
+  CREATE VIRTUAL TABLE t2 USING fts5(a, b);
+  INSERT INTO t2 VALUES('m f a jj th q jr ar',   'hj n h h sg j i m');
+  INSERT INTO t2 VALUES('nr s t g od j kf h',    'sb h aq rg op rb n nl');
+  INSERT INTO t2 VALUES('do h h pb p p q fr',    'c rj qs or cr a l i');
+  INSERT INTO t2 VALUES('lk gp t i lq mq qm p',  'h mr g f op ld aj h');
+  INSERT INTO t2 VALUES('ct d sq kc qi k f j',   'sn gh c of g s qt q');
+  INSERT INTO t2 VALUES('d ea d d om mp s ab',   'dm hg l df cm ft pa c');
+  INSERT INTO t2 VALUES('tc dk c jn n t sr ge',  'a a kn bc n i af h');
+  INSERT INTO t2 VALUES('ie ii d i b sa qo rf',  'a h m aq i b m fn');
+  INSERT INTO t2 VALUES('gs r fo a er m h li',   'tm c p gl eb ml q r');
+  INSERT INTO t2 VALUES('k fe fd rd a gi ho kk', 'ng m c r d ml rm r');
+}
+faultsim_save_and_close
 
+foreach {tn expr res} {
+  1 { dk  }           7
+  2 { m f }           1
+  3 { f*  }           {10 9 8 6 5 4 3 1}
+  4 { m OR f }        {10 9 8 5 4 1}
+  5 { sn + gh }       {5}
+  6 { "sn gh" }       {5}
+  7 { NEAR(r a, 5) }  {9}
+} {
+  do_faultsim_test 4.$tn -prep {
+    faultsim_restore_and_reopen
+  } -body "
+    execsql { SELECT rowid FROM t2 WHERE t2 MATCH '$expr' }
+  " -test "
+    faultsim_test_result {[list 0 $res]}
+  "
+}
 
 finish_test
+
index 160897057af0ae0df656f80572e5b529ae7a2c83..1393a84f350be615976ee155e8faace3644ebeaf 100644 (file)
@@ -129,6 +129,8 @@ proc do_faultsim_test {name args} {
   set DEFAULT(-test)          ""
   set DEFAULT(-install)       ""
   set DEFAULT(-uninstall)     ""
+  set DEFAULT(-start)          1
+  set DEFAULT(-end)            0
 
   fix_testname name
 
@@ -146,7 +148,8 @@ proc do_faultsim_test {name args} {
   }
 
   set testspec [list -prep $O(-prep) -body $O(-body) \
-      -test $O(-test) -install $O(-install) -uninstall $O(-uninstall)
+      -test $O(-test) -install $O(-install) -uninstall $O(-uninstall) \
+      -start $O(-start) -end $O(-end)
   ]
   foreach f [lsort -unique $faultlist] {
     eval do_one_faultsim_test "$name-$f" $FAULTSIM($f) $testspec
@@ -318,6 +321,8 @@ proc faultsim_test_result_int {args} {
 #
 #     -test             Script to execute after -body.
 #
+#     -start            Index of first fault to inject (default 1)
+#
 proc do_one_faultsim_test {testname args} {
 
   set DEFAULT(-injectstart)     "expr"
@@ -330,6 +335,8 @@ proc do_one_faultsim_test {testname args} {
   set DEFAULT(-test)            ""
   set DEFAULT(-install)         ""
   set DEFAULT(-uninstall)       ""
+  set DEFAULT(-start)           1
+  set DEFAULT(-end)             0
 
   array set O [array get DEFAULT]
   array set O $args
@@ -346,7 +353,10 @@ proc do_one_faultsim_test {testname args} {
   eval $O(-install)
 
   set stop 0
-  for {set iFail 1} {!$stop} {incr iFail} {
+  for {set iFail $O(-start)}                        \
+      {!$stop && ($O(-end)==0 || $iFail<=$O(-end))} \
+      {incr iFail}                                  \
+  {
 
     # Evaluate the -prep script.
     #