Fix various problems in fts5 revealed by fault-injection tests.
FossilOrigin-Name: e358c3de5c916f2c851ab9324ceaae4e4e7a0fbd
This commit is contained in:
+1
-1
@@ -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;
|
||||
|
||||
|
||||
+11
-8
@@ -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;
|
||||
|
||||
+16
-9
@@ -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);
|
||||
|
||||
+185
-158
@@ -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(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(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( 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( 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 */
|
||||
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; iLvl<pStruct->nLevel; 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; 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;
|
||||
}
|
||||
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){
|
||||
|
||||
@@ -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
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
2037dba62fdd995ad15b642abe499a790f5ffe5c
|
||||
e358c3de5c916f2c851ab9324ceaae4e4e7a0fbd
|
||||
+4
-3
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+75
-1
@@ -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
|
||||
|
||||
|
||||
+12
-2
@@ -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.
|
||||
#
|
||||
|
||||
Reference in New Issue
Block a user