From e4bec37900581a9d6048bfd0f4339362569390f8 Mon Sep 17 00:00:00 2001 From: dan Date: Thu, 18 Dec 2014 18:25:48 +0000 Subject: [PATCH] Fix various problems in fts5 revealed by fault-injection tests. FossilOrigin-Name: e358c3de5c916f2c851ab9324ceaae4e4e7a0fbd --- ext/fts5/fts5.c | 2 +- ext/fts5/fts5_config.c | 19 ++- ext/fts5/fts5_expr.c | 25 +-- ext/fts5/fts5_index.c | 343 ++++++++++++++++++++++------------------- manifest | 24 +-- manifest.uuid | 2 +- src/vtab.c | 7 +- test/fts5fault1.test | 76 ++++++++- test/malloc_common.tcl | 14 +- 9 files changed, 317 insertions(+), 195 deletions(-) diff --git a/ext/fts5/fts5.c b/ext/fts5/fts5.c index e80715a4c7..4c6e98b86e 100644 --- a/ext/fts5/fts5.c +++ b/ext/fts5/fts5.c @@ -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; diff --git a/ext/fts5/fts5_config.c b/ext/fts5/fts5_config.c index 9ea78143c5..54c7a57f28 100644 --- a/ext/fts5/fts5_config.c +++ b/ext/fts5/fts5_config.c @@ -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; diff --git a/ext/fts5/fts5_expr.c b/ext/fts5/fts5_expr.c index 5c95eda7e4..830af586b3 100644 --- a/ext/fts5/fts5_expr.c +++ b/ext/fts5/fts5_expr.c @@ -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); diff --git a/ext/fts5/fts5_index.c b/ext/fts5/fts5_index.c index 7d0d01afdf..95b2cdb2f7 100644 --- a/ext/fts5/fts5_index.c +++ b/ext/fts5/fts5_index.c @@ -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; inLevel; 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; inLevel; 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; iLvlnLevel; 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(iLvl=0; iLvlnLevel; 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; iSegnSeg; 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(iSeg=0; iSegnSeg; 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( 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); - } + /* 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( iRowidiPrevRowid ); - 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 || iRowidiPrevRowid ); + 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; inWriter; 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; inWriter; 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; inWriter; 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 */ + 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; + /* 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 */ + 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; iLvlnLevel; iLvl++){ - Fts5StructureLevel *pLvl = &pStruct->aLevel[iLvl]; - if( pLvl->nMerge ){ - if( pLvl->nMerge>nBest ){ - iBestLvl = iLvl; - nBest = pLvl->nMerge; + /* Set iBestLvl to the level to read input segments from. */ + assert( pStruct->nLevel>0 ); + for(iLvl=0; iLvlnLevel; 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; } - 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 && iLvlnLevel; iLvl++){ - assert( pStruct->aLevel[iLvl].nSeg==0 ); - } + for(iLvl=0; nBest==0 && iLvlnLevel; iLvl++){ + assert( pStruct->aLevel[iLvl].nSeg==0 ); + } #endif - if( nBestpConfig->nAutomerge - && pStruct->aLevel[iBestLvl].nMerge==0 - ){ - break; + if( nBestpConfig->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){ diff --git a/manifest b/manifest index a2dd5734ff..a7170b5cfd 100644 --- 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 diff --git a/manifest.uuid b/manifest.uuid index f14250e59b..47e70fa938 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -2037dba62fdd995ad15b642abe499a790f5ffe5c \ No newline at end of file +e358c3de5c916f2c851ab9324ceaae4e4e7a0fbd \ No newline at end of file diff --git a/src/vtab.c b/src/vtab.c index ca0db214cc..7b4867f9c0 100644 --- a/src/vtab.c +++ b/src/vtab.c @@ -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; inVTrans; 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; } } diff --git a/test/fts5fault1.test b/test/fts5fault1.test index 723ae3d22f..76434b3a80 100644 --- a/test/fts5fault1.test +++ b/test/fts5fault1.test @@ -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 + diff --git a/test/malloc_common.tcl b/test/malloc_common.tcl index 160897057a..1393a84f35 100644 --- a/test/malloc_common.tcl +++ b/test/malloc_common.tcl @@ -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. #