Index: third_party/sqlite/src/ext/fts3/fts3.c |
diff --git a/third_party/sqlite/src/ext/fts3/fts3.c b/third_party/sqlite/src/ext/fts3/fts3.c |
index c43f3696ff4d50621501369b70e7563c954087fe..a8d16a9a2163a105c3d693b00223447e0e8105cb 100644 |
--- a/third_party/sqlite/src/ext/fts3/fts3.c |
+++ b/third_party/sqlite/src/ext/fts3/fts3.c |
@@ -314,6 +314,13 @@ static int fts3EvalStart(Fts3Cursor *pCsr); |
static int fts3TermSegReaderCursor( |
Fts3Cursor *, const char *, int, int, Fts3MultiSegReader **); |
+#ifndef SQLITE_AMALGAMATION |
+# if defined(SQLITE_DEBUG) |
+int sqlite3Fts3Always(int b) { assert( b ); return b; } |
+int sqlite3Fts3Never(int b) { assert( !b ); return b; } |
+# endif |
+#endif |
+ |
/* |
** Write a 64-bit variable-length integer to memory starting at p[0]. |
** The length of data written will be between 1 and FTS3_VARINT_MAX bytes. |
@@ -423,7 +430,7 @@ void sqlite3Fts3Dequote(char *z){ |
/* If the first byte was a '[', then the close-quote character is a ']' */ |
if( quote=='[' ) quote = ']'; |
- while( ALWAYS(z[iIn]) ){ |
+ while( z[iIn] ){ |
if( z[iIn]==quote ){ |
if( z[iIn+1]!=quote ) break; |
z[iOut++] = quote; |
@@ -503,6 +510,17 @@ static int fts3DisconnectMethod(sqlite3_vtab *pVtab){ |
} |
/* |
+** Write an error message into *pzErr |
+*/ |
+void sqlite3Fts3ErrMsg(char **pzErr, const char *zFormat, ...){ |
+ va_list ap; |
+ sqlite3_free(*pzErr); |
+ va_start(ap, zFormat); |
+ *pzErr = sqlite3_vmprintf(zFormat, ap); |
+ va_end(ap); |
+} |
+ |
+/* |
** Construct one or more SQL statements from the format string given |
** and then evaluate those statements. The success code is written |
** into *pRc. |
@@ -911,11 +929,16 @@ static char *fts3WriteExprList(Fts3Table *p, const char *zFunc, int *pRc){ |
** This function is used when parsing the "prefix=" FTS4 parameter. |
*/ |
static int fts3GobbleInt(const char **pp, int *pnOut){ |
+ const int MAX_NPREFIX = 10000000; |
const char *p; /* Iterator pointer */ |
int nInt = 0; /* Output value */ |
for(p=*pp; p[0]>='0' && p[0]<='9'; p++){ |
nInt = nInt * 10 + (p[0] - '0'); |
+ if( nInt>MAX_NPREFIX ){ |
+ nInt = 0; |
+ break; |
+ } |
} |
if( p==*pp ) return SQLITE_ERROR; |
*pnOut = nInt; |
@@ -958,7 +981,6 @@ static int fts3PrefixParameter( |
aIndex = sqlite3_malloc(sizeof(struct Fts3Index) * nIndex); |
*apIndex = aIndex; |
- *pnIndex = nIndex; |
if( !aIndex ){ |
return SQLITE_NOMEM; |
} |
@@ -968,13 +990,20 @@ static int fts3PrefixParameter( |
const char *p = zParam; |
int i; |
for(i=1; i<nIndex; i++){ |
- int nPrefix; |
+ int nPrefix = 0; |
if( fts3GobbleInt(&p, &nPrefix) ) return SQLITE_ERROR; |
- aIndex[i].nPrefix = nPrefix; |
+ assert( nPrefix>=0 ); |
+ if( nPrefix==0 ){ |
+ nIndex--; |
+ i--; |
+ }else{ |
+ aIndex[i].nPrefix = nPrefix; |
+ } |
p++; |
} |
} |
+ *pnIndex = nIndex; |
return SQLITE_OK; |
} |
@@ -1009,7 +1038,8 @@ static int fts3ContentColumns( |
const char *zTbl, /* Name of content table */ |
const char ***pazCol, /* OUT: Malloc'd array of column names */ |
int *pnCol, /* OUT: Size of array *pazCol */ |
- int *pnStr /* OUT: Bytes of string content */ |
+ int *pnStr, /* OUT: Bytes of string content */ |
+ char **pzErr /* OUT: error message */ |
){ |
int rc = SQLITE_OK; /* Return code */ |
char *zSql; /* "SELECT *" statement on zTbl */ |
@@ -1020,6 +1050,9 @@ static int fts3ContentColumns( |
rc = SQLITE_NOMEM; |
}else{ |
rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0); |
+ if( rc!=SQLITE_OK ){ |
+ sqlite3Fts3ErrMsg(pzErr, "%s", sqlite3_errmsg(db)); |
+ } |
} |
sqlite3_free(zSql); |
@@ -1098,7 +1131,7 @@ static int fts3InitVtab( |
const char **aCol; /* Array of column names */ |
sqlite3_tokenizer *pTokenizer = 0; /* Tokenizer for this table */ |
- int nIndex; /* Size of aIndex[] array */ |
+ int nIndex = 0; /* Size of aIndex[] array */ |
struct Fts3Index *aIndex = 0; /* Array of indexes for this table */ |
/* The results of parsing supported FTS4 key=value options: */ |
@@ -1186,13 +1219,13 @@ static int fts3InitVtab( |
} |
} |
if( iOpt==SizeofArray(aFts4Opt) ){ |
- *pzErr = sqlite3_mprintf("unrecognized parameter: %s", z); |
+ sqlite3Fts3ErrMsg(pzErr, "unrecognized parameter: %s", z); |
rc = SQLITE_ERROR; |
}else{ |
switch( iOpt ){ |
case 0: /* MATCHINFO */ |
if( strlen(zVal)!=4 || sqlite3_strnicmp(zVal, "fts3", 4) ){ |
- *pzErr = sqlite3_mprintf("unrecognized matchinfo: %s", zVal); |
+ sqlite3Fts3ErrMsg(pzErr, "unrecognized matchinfo: %s", zVal); |
rc = SQLITE_ERROR; |
} |
bNoDocsize = 1; |
@@ -1220,7 +1253,7 @@ static int fts3InitVtab( |
if( (strlen(zVal)!=3 || sqlite3_strnicmp(zVal, "asc", 3)) |
&& (strlen(zVal)!=4 || sqlite3_strnicmp(zVal, "desc", 4)) |
){ |
- *pzErr = sqlite3_mprintf("unrecognized order: %s", zVal); |
+ sqlite3Fts3ErrMsg(pzErr, "unrecognized order: %s", zVal); |
rc = SQLITE_ERROR; |
} |
bDescIdx = (zVal[0]=='d' || zVal[0]=='D'); |
@@ -1271,7 +1304,7 @@ static int fts3InitVtab( |
if( nCol==0 ){ |
sqlite3_free((void*)aCol); |
aCol = 0; |
- rc = fts3ContentColumns(db, argv[1], zContent, &aCol, &nCol, &nString); |
+ rc = fts3ContentColumns(db, argv[1], zContent,&aCol,&nCol,&nString,pzErr); |
/* If a languageid= option was specified, remove the language id |
** column from the aCol[] array. */ |
@@ -1306,7 +1339,7 @@ static int fts3InitVtab( |
rc = fts3PrefixParameter(zPrefix, &nIndex, &aIndex); |
if( rc==SQLITE_ERROR ){ |
assert( zPrefix ); |
- *pzErr = sqlite3_mprintf("error parsing prefix parameter: %s", zPrefix); |
+ sqlite3Fts3ErrMsg(pzErr, "error parsing prefix parameter: %s", zPrefix); |
} |
if( rc!=SQLITE_OK ) goto fts3_init_out; |
@@ -1388,7 +1421,7 @@ static int fts3InitVtab( |
} |
for(i=0; i<nNotindexed; i++){ |
if( azNotindexed[i] ){ |
- *pzErr = sqlite3_mprintf("no such column: %s", azNotindexed[i]); |
+ sqlite3Fts3ErrMsg(pzErr, "no such column: %s", azNotindexed[i]); |
rc = SQLITE_ERROR; |
} |
} |
@@ -1396,7 +1429,7 @@ static int fts3InitVtab( |
if( rc==SQLITE_OK && (zCompress==0)!=(zUncompress==0) ){ |
char const *zMiss = (zCompress==0 ? "compress" : "uncompress"); |
rc = SQLITE_ERROR; |
- *pzErr = sqlite3_mprintf("missing %s parameter in fts4 constructor", zMiss); |
+ sqlite3Fts3ErrMsg(pzErr, "missing %s parameter in fts4 constructor", zMiss); |
} |
p->zReadExprlist = fts3ReadExprList(p, zUncompress, &rc); |
p->zWriteExprlist = fts3WriteExprList(p, zCompress, &rc); |
@@ -1485,6 +1518,19 @@ static void fts3SetEstimatedRows(sqlite3_index_info *pIdxInfo, i64 nRow){ |
#endif |
} |
+/* |
+** Set the SQLITE_INDEX_SCAN_UNIQUE flag in pIdxInfo->flags. Unless this |
+** extension is currently being used by a version of SQLite too old to |
+** support index-info flags. In that case this function is a no-op. |
+*/ |
+static void fts3SetUniqueFlag(sqlite3_index_info *pIdxInfo){ |
+#if SQLITE_VERSION_NUMBER>=3008012 |
+ if( sqlite3_libversion_number()>=3008012 ){ |
+ pIdxInfo->idxFlags |= SQLITE_INDEX_SCAN_UNIQUE; |
+ } |
+#endif |
+} |
+ |
/* |
** Implementation of the xBestIndex method for FTS3 tables. There |
** are three possible strategies, in order of preference: |
@@ -1575,6 +1621,9 @@ static int fts3BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){ |
} |
} |
+ /* If using a docid=? or rowid=? strategy, set the UNIQUE flag. */ |
+ if( pInfo->idxNum==FTS3_DOCID_SEARCH ) fts3SetUniqueFlag(pInfo); |
+ |
iIdx = 1; |
if( iCons>=0 ){ |
pInfo->aConstraintUsage[iCons].argvIndex = iIdx++; |
@@ -1643,7 +1692,7 @@ static int fts3CloseMethod(sqlite3_vtab_cursor *pCursor){ |
sqlite3Fts3ExprFree(pCsr->pExpr); |
sqlite3Fts3FreeDeferredTokens(pCsr); |
sqlite3_free(pCsr->aDoclist); |
- sqlite3_free(pCsr->aMatchinfo); |
+ sqlite3Fts3MIBufferFree(pCsr->pMIBuffer); |
assert( ((Fts3Table *)pCsr->base.pVtab)->pSegments==0 ); |
sqlite3_free(pCsr); |
return SQLITE_OK; |
@@ -1860,7 +1909,7 @@ static int fts3SelectLeaf( |
sqlite3_int64 *piLeaf, /* Selected leaf node */ |
sqlite3_int64 *piLeaf2 /* Selected leaf node */ |
){ |
- int rc; /* Return code */ |
+ int rc = SQLITE_OK; /* Return code */ |
int iHeight; /* Height of this node in tree */ |
assert( piLeaf || piLeaf2 ); |
@@ -1871,7 +1920,7 @@ static int fts3SelectLeaf( |
if( rc==SQLITE_OK && iHeight>1 ){ |
char *zBlob = 0; /* Blob read from %_segments table */ |
- int nBlob; /* Size of zBlob in bytes */ |
+ int nBlob = 0; /* Size of zBlob in bytes */ |
if( piLeaf && piLeaf2 && (*piLeaf!=*piLeaf2) ){ |
rc = sqlite3Fts3ReadBlock(p, *piLeaf, &zBlob, &nBlob, 0); |
@@ -2498,26 +2547,33 @@ static int fts3DoclistOrMerge( |
** |
** The right-hand input doclist is overwritten by this function. |
*/ |
-static void fts3DoclistPhraseMerge( |
+static int fts3DoclistPhraseMerge( |
int bDescDoclist, /* True if arguments are desc */ |
int nDist, /* Distance from left to right (1=adjacent) */ |
char *aLeft, int nLeft, /* Left doclist */ |
- char *aRight, int *pnRight /* IN/OUT: Right/output doclist */ |
+ char **paRight, int *pnRight /* IN/OUT: Right/output doclist */ |
){ |
sqlite3_int64 i1 = 0; |
sqlite3_int64 i2 = 0; |
sqlite3_int64 iPrev = 0; |
+ char *aRight = *paRight; |
char *pEnd1 = &aLeft[nLeft]; |
char *pEnd2 = &aRight[*pnRight]; |
char *p1 = aLeft; |
char *p2 = aRight; |
char *p; |
int bFirstOut = 0; |
- char *aOut = aRight; |
+ char *aOut; |
assert( nDist>0 ); |
- |
+ if( bDescDoclist ){ |
+ aOut = sqlite3_malloc(*pnRight + FTS3_VARINT_MAX); |
+ if( aOut==0 ) return SQLITE_NOMEM; |
+ }else{ |
+ aOut = aRight; |
+ } |
p = aOut; |
+ |
fts3GetDeltaVarint3(&p1, pEnd1, 0, &i1); |
fts3GetDeltaVarint3(&p2, pEnd2, 0, &i2); |
@@ -2546,6 +2602,12 @@ static void fts3DoclistPhraseMerge( |
} |
*pnRight = (int)(p - aOut); |
+ if( bDescDoclist ){ |
+ sqlite3_free(aRight); |
+ *paRight = aOut; |
+ } |
+ |
+ return SQLITE_OK; |
} |
/* |
@@ -2670,8 +2732,22 @@ static int fts3TermSelectMerge( |
){ |
if( pTS->aaOutput[0]==0 ){ |
/* If this is the first term selected, copy the doclist to the output |
- ** buffer using memcpy(). */ |
- pTS->aaOutput[0] = sqlite3_malloc(nDoclist); |
+ ** buffer using memcpy(). |
+ ** |
+ ** Add FTS3_VARINT_MAX bytes of unused space to the end of the |
+ ** allocation. This is so as to ensure that the buffer is big enough |
+ ** to hold the current doclist AND'd with any other doclist. If the |
+ ** doclists are stored in order=ASC order, this padding would not be |
+ ** required (since the size of [doclistA AND doclistB] is always less |
+ ** than or equal to the size of [doclistA] in that case). But this is |
+ ** not true for order=DESC. For example, a doclist containing (1, -1) |
+ ** may be smaller than (-1), as in the first example the -1 may be stored |
+ ** as a single-byte delta, whereas in the second it must be stored as a |
+ ** FTS3_VARINT_MAX byte varint. |
+ ** |
+ ** Similar padding is added in the fts3DoclistOrMerge() function. |
+ */ |
+ pTS->aaOutput[0] = sqlite3_malloc(nDoclist + FTS3_VARINT_MAX + 1); |
pTS->anOutput[0] = nDoclist; |
if( pTS->aaOutput[0] ){ |
memcpy(pTS->aaOutput[0], aDoclist, nDoclist); |
@@ -2768,7 +2844,7 @@ static int fts3SegReaderCursor( |
** calls out here. */ |
if( iLevel<0 && p->aIndex ){ |
Fts3SegReader *pSeg = 0; |
- rc = sqlite3Fts3SegReaderPending(p, iIndex, zTerm, nTerm, isPrefix, &pSeg); |
+ rc = sqlite3Fts3SegReaderPending(p, iIndex, zTerm, nTerm, isPrefix||isScan, &pSeg); |
if( rc==SQLITE_OK && pSeg ){ |
rc = fts3SegReaderCursorAppend(pCsr, pSeg); |
} |
@@ -3093,7 +3169,7 @@ static int fts3FilterMethod( |
int nVal, /* Number of elements in apVal */ |
sqlite3_value **apVal /* Arguments for the indexing scheme */ |
){ |
- int rc; |
+ int rc = SQLITE_OK; |
char *zSql; /* SQL statement used to access %_content */ |
int eSearch; |
Fts3Table *p = (Fts3Table *)pCursor->pVtab; |
@@ -3123,7 +3199,7 @@ static int fts3FilterMethod( |
/* In case the cursor has been used before, clear it now. */ |
sqlite3_finalize(pCsr->pStmt); |
sqlite3_free(pCsr->aDoclist); |
- sqlite3_free(pCsr->aMatchinfo); |
+ sqlite3Fts3MIBufferFree(pCsr->pMIBuffer); |
sqlite3Fts3ExprFree(pCsr->pExpr); |
memset(&pCursor[1], 0, sizeof(Fts3Cursor)-sizeof(sqlite3_vtab_cursor)); |
@@ -3171,10 +3247,17 @@ static int fts3FilterMethod( |
** row by docid. |
*/ |
if( eSearch==FTS3_FULLSCAN_SEARCH ){ |
- zSql = sqlite3_mprintf( |
- "SELECT %s ORDER BY rowid %s", |
- p->zReadExprlist, (pCsr->bDesc ? "DESC" : "ASC") |
- ); |
+ if( pDocidGe || pDocidLe ){ |
+ zSql = sqlite3_mprintf( |
+ "SELECT %s WHERE rowid BETWEEN %lld AND %lld ORDER BY rowid %s", |
+ p->zReadExprlist, pCsr->iMinDocid, pCsr->iMaxDocid, |
+ (pCsr->bDesc ? "DESC" : "ASC") |
+ ); |
+ }else{ |
+ zSql = sqlite3_mprintf("SELECT %s ORDER BY rowid %s", |
+ p->zReadExprlist, (pCsr->bDesc ? "DESC" : "ASC") |
+ ); |
+ } |
if( zSql ){ |
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pCsr->pStmt, 0); |
sqlite3_free(zSql); |
@@ -3410,11 +3493,31 @@ static void fts3ReversePoslist(char *pStart, char **ppPoslist){ |
char *p = &(*ppPoslist)[-2]; |
char c = 0; |
+ /* Skip backwards passed any trailing 0x00 bytes added by NearTrim() */ |
while( p>pStart && (c=*p--)==0 ); |
+ |
+ /* Search backwards for a varint with value zero (the end of the previous |
+ ** poslist). This is an 0x00 byte preceded by some byte that does not |
+ ** have the 0x80 bit set. */ |
while( p>pStart && (*p & 0x80) | c ){ |
c = *p--; |
} |
- if( p>pStart ){ p = &p[2]; } |
+ assert( p==pStart || c==0 ); |
+ |
+ /* At this point p points to that preceding byte without the 0x80 bit |
+ ** set. So to find the start of the poslist, skip forward 2 bytes then |
+ ** over a varint. |
+ ** |
+ ** Normally. The other case is that p==pStart and the poslist to return |
+ ** is the first in the doclist. In this case do not skip forward 2 bytes. |
+ ** The second part of the if condition (c==0 && *ppPoslist>&p[2]) |
+ ** is required for cases where the first byte of a doclist and the |
+ ** doclist is empty. For example, if the first docid is 10, a doclist |
+ ** that begins with: |
+ ** |
+ ** 0x0A 0x00 <next docid delta varint> |
+ */ |
+ if( p>pStart || (c==0 && *ppPoslist>&p[2]) ){ p = &p[2]; } |
while( *p++&0x80 ); |
*ppPoslist = p; |
} |
@@ -3485,6 +3588,8 @@ static void fts3SnippetFunc( |
} |
if( !zEllipsis || !zEnd || !zStart ){ |
sqlite3_result_error_nomem(pContext); |
+ }else if( nToken==0 ){ |
+ sqlite3_result_text(pContext, "", -1, SQLITE_STATIC); |
}else if( SQLITE_OK==fts3CursorSeek(pContext, pCsr) ){ |
sqlite3Fts3Snippet(pContext, pCsr, zStart, zEnd, zEllipsis, iCol, nToken); |
} |
@@ -3928,14 +4033,17 @@ static void fts3EvalAllocateReaders( |
** This function assumes that pList points to a buffer allocated using |
** sqlite3_malloc(). This function takes responsibility for eventually |
** freeing the buffer. |
+** |
+** SQLITE_OK is returned if successful, or SQLITE_NOMEM if an error occurs. |
*/ |
-static void fts3EvalPhraseMergeToken( |
+static int fts3EvalPhraseMergeToken( |
Fts3Table *pTab, /* FTS Table pointer */ |
Fts3Phrase *p, /* Phrase to merge pList/nList into */ |
int iToken, /* Token pList/nList corresponds to */ |
char *pList, /* Pointer to doclist */ |
int nList /* Number of bytes in pList */ |
){ |
+ int rc = SQLITE_OK; |
assert( iToken!=p->iDoclistToken ); |
if( pList==0 ){ |
@@ -3974,13 +4082,16 @@ static void fts3EvalPhraseMergeToken( |
nDiff = p->iDoclistToken - iToken; |
} |
- fts3DoclistPhraseMerge(pTab->bDescIdx, nDiff, pLeft, nLeft, pRight,&nRight); |
+ rc = fts3DoclistPhraseMerge( |
+ pTab->bDescIdx, nDiff, pLeft, nLeft, &pRight, &nRight |
+ ); |
sqlite3_free(pLeft); |
p->doclist.aAll = pRight; |
p->doclist.nAll = nRight; |
} |
if( iToken>p->iDoclistToken ) p->iDoclistToken = iToken; |
+ return rc; |
} |
/* |
@@ -4006,7 +4117,7 @@ static int fts3EvalPhraseLoad( |
char *pThis = 0; |
rc = fts3TermSelect(pTab, pToken, p->iColumn, &nThis, &pThis); |
if( rc==SQLITE_OK ){ |
- fts3EvalPhraseMergeToken(pTab, p, iToken, pThis, nThis); |
+ rc = fts3EvalPhraseMergeToken(pTab, p, iToken, pThis, nThis); |
} |
} |
assert( pToken->pSegcsr==0 ); |
@@ -4151,7 +4262,6 @@ static int fts3EvalPhraseStart(Fts3Cursor *pCsr, int bOptOk, Fts3Phrase *p){ |
int bIncrOk = (bOptOk |
&& pCsr->bDesc==pTab->bDescIdx |
&& p->nToken<=MAX_INCR_PHRASE_TOKENS && p->nToken>0 |
- && p->nToken<=MAX_INCR_PHRASE_TOKENS && p->nToken>0 |
#ifdef SQLITE_TEST |
&& pTab->bNoIncrDoclist==0 |
#endif |
@@ -4271,6 +4381,7 @@ void sqlite3Fts3DoclistNext( |
p += sqlite3Fts3GetVarint(p, piDocid); |
}else{ |
fts3PoslistCopy(0, &p); |
+ while( p<&aDoclist[nDoclist] && *p==0 ) p++; |
if( p>=&aDoclist[nDoclist] ){ |
*pbEof = 1; |
}else{ |
@@ -4548,12 +4659,14 @@ static void fts3EvalStartReaders( |
){ |
if( pExpr && SQLITE_OK==*pRc ){ |
if( pExpr->eType==FTSQUERY_PHRASE ){ |
- int i; |
int nToken = pExpr->pPhrase->nToken; |
- for(i=0; i<nToken; i++){ |
- if( pExpr->pPhrase->aToken[i].pDeferred==0 ) break; |
+ if( nToken ){ |
+ int i; |
+ for(i=0; i<nToken; i++){ |
+ if( pExpr->pPhrase->aToken[i].pDeferred==0 ) break; |
+ } |
+ pExpr->bDeferred = (i==nToken); |
} |
- pExpr->bDeferred = (i==nToken); |
*pRc = fts3EvalPhraseStart(pCsr, 1, pExpr->pPhrase); |
}else{ |
fts3EvalStartReaders(pCsr, pExpr->pLeft, pRc); |
@@ -4809,8 +4922,12 @@ static int fts3EvalSelectDeferred( |
rc = fts3TermSelect(pTab, pToken, pTC->iCol, &nList, &pList); |
assert( rc==SQLITE_OK || pList==0 ); |
if( rc==SQLITE_OK ){ |
+ rc = fts3EvalPhraseMergeToken( |
+ pTab, pTC->pPhrase, pTC->iToken,pList,nList |
+ ); |
+ } |
+ if( rc==SQLITE_OK ){ |
int nCount; |
- fts3EvalPhraseMergeToken(pTab, pTC->pPhrase, pTC->iToken,pList,nList); |
nCount = fts3DoclistCountDocids( |
pTC->pPhrase->doclist.aAll, pTC->pPhrase->doclist.nAll |
); |
@@ -4988,7 +5105,7 @@ static int fts3EvalNearTrim( |
** 2. NEAR is treated as AND. If the expression is "x NEAR y", it is |
** advanced to point to the next row that matches "x AND y". |
** |
-** See fts3EvalTestDeferredAndNear() for details on testing if a row is |
+** See sqlite3Fts3EvalTestDeferred() for details on testing if a row is |
** really a match, taking into account deferred tokens and NEAR operators. |
*/ |
static void fts3EvalNextRow( |
@@ -5035,6 +5152,22 @@ static void fts3EvalNextRow( |
} |
pExpr->iDocid = pLeft->iDocid; |
pExpr->bEof = (pLeft->bEof || pRight->bEof); |
+ if( pExpr->eType==FTSQUERY_NEAR && pExpr->bEof ){ |
+ if( pRight->pPhrase && pRight->pPhrase->doclist.aAll ){ |
+ Fts3Doclist *pDl = &pRight->pPhrase->doclist; |
+ while( *pRc==SQLITE_OK && pRight->bEof==0 ){ |
+ memset(pDl->pList, 0, pDl->nList); |
+ fts3EvalNextRow(pCsr, pRight, pRc); |
+ } |
+ } |
+ if( pLeft->pPhrase && pLeft->pPhrase->doclist.aAll ){ |
+ Fts3Doclist *pDl = &pLeft->pPhrase->doclist; |
+ while( *pRc==SQLITE_OK && pLeft->bEof==0 ){ |
+ memset(pDl->pList, 0, pDl->nList); |
+ fts3EvalNextRow(pCsr, pLeft, pRc); |
+ } |
+ } |
+ } |
} |
break; |
} |
@@ -5192,7 +5325,7 @@ static int fts3EvalNearTest(Fts3Expr *pExpr, int *pRc){ |
} |
/* |
-** This function is a helper function for fts3EvalTestDeferredAndNear(). |
+** This function is a helper function for sqlite3Fts3EvalTestDeferred(). |
** Assuming no error occurs or has occurred, It returns non-zero if the |
** expression passed as the second argument matches the row that pCsr |
** currently points to, or zero if it does not. |
@@ -5313,7 +5446,7 @@ static int fts3EvalTestExpr( |
** Or, if no error occurs and it seems the current row does match the FTS |
** query, return 0. |
*/ |
-static int fts3EvalTestDeferredAndNear(Fts3Cursor *pCsr, int *pRc){ |
+int sqlite3Fts3EvalTestDeferred(Fts3Cursor *pCsr, int *pRc){ |
int rc = *pRc; |
int bMiss = 0; |
if( rc==SQLITE_OK ){ |
@@ -5360,7 +5493,7 @@ static int fts3EvalNext(Fts3Cursor *pCsr){ |
pCsr->isRequireSeek = 1; |
pCsr->isMatchinfoNeeded = 1; |
pCsr->iPrevId = pExpr->iDocid; |
- }while( pCsr->isEof==0 && fts3EvalTestDeferredAndNear(pCsr, &rc) ); |
+ }while( pCsr->isEof==0 && sqlite3Fts3EvalTestDeferred(pCsr, &rc) ); |
} |
/* Check if the cursor is past the end of the docid range specified |
@@ -5407,6 +5540,7 @@ static void fts3EvalRestart( |
} |
pPhrase->doclist.pNextDocid = 0; |
pPhrase->doclist.iDocid = 0; |
+ pPhrase->pOrPoslist = 0; |
} |
pExpr->iDocid = 0; |
@@ -5520,7 +5654,7 @@ static int fts3EvalGatherStats( |
pCsr->iPrevId = pRoot->iDocid; |
}while( pCsr->isEof==0 |
&& pRoot->eType==FTSQUERY_NEAR |
- && fts3EvalTestDeferredAndNear(pCsr, &rc) |
+ && sqlite3Fts3EvalTestDeferred(pCsr, &rc) |
); |
if( rc==SQLITE_OK && pCsr->isEof==0 ){ |
@@ -5545,7 +5679,6 @@ static int fts3EvalGatherStats( |
fts3EvalNextRow(pCsr, pRoot, &rc); |
assert( pRoot->bEof==0 ); |
}while( pRoot->iDocid!=iDocid && rc==SQLITE_OK ); |
- fts3EvalTestDeferredAndNear(pCsr, &rc); |
} |
} |
return rc; |
@@ -5652,13 +5785,13 @@ int sqlite3Fts3EvalPhrasePoslist( |
iDocid = pExpr->iDocid; |
pIter = pPhrase->doclist.pList; |
if( iDocid!=pCsr->iPrevId || pExpr->bEof ){ |
+ int rc = SQLITE_OK; |
int bDescDoclist = pTab->bDescIdx; /* For DOCID_CMP macro */ |
- int iMul; /* +1 if csr dir matches index dir, else -1 */ |
int bOr = 0; |
- u8 bEof = 0; |
u8 bTreeEof = 0; |
Fts3Expr *p; /* Used to iterate from pExpr to root */ |
Fts3Expr *pNear; /* Most senior NEAR ancestor (or pExpr) */ |
+ int bMatch; |
/* Check if this phrase descends from an OR expression node. If not, |
** return NULL. Otherwise, the entry that corresponds to docid |
@@ -5677,74 +5810,62 @@ int sqlite3Fts3EvalPhrasePoslist( |
** an incremental phrase. Load the entire doclist for the phrase |
** into memory in this case. */ |
if( pPhrase->bIncr ){ |
- int rc = SQLITE_OK; |
- int bEofSave = pExpr->bEof; |
- fts3EvalRestart(pCsr, pExpr, &rc); |
- while( rc==SQLITE_OK && !pExpr->bEof ){ |
- fts3EvalNextRow(pCsr, pExpr, &rc); |
- if( bEofSave==0 && pExpr->iDocid==iDocid ) break; |
+ int bEofSave = pNear->bEof; |
+ fts3EvalRestart(pCsr, pNear, &rc); |
+ while( rc==SQLITE_OK && !pNear->bEof ){ |
+ fts3EvalNextRow(pCsr, pNear, &rc); |
+ if( bEofSave==0 && pNear->iDocid==iDocid ) break; |
} |
- pIter = pPhrase->doclist.pList; |
assert( rc!=SQLITE_OK || pPhrase->bIncr==0 ); |
- if( rc!=SQLITE_OK ) return rc; |
} |
- |
- iMul = ((pCsr->bDesc==bDescDoclist) ? 1 : -1); |
- while( bTreeEof==1 |
- && pNear->bEof==0 |
- && (DOCID_CMP(pNear->iDocid, pCsr->iPrevId) * iMul)<0 |
- ){ |
- int rc = SQLITE_OK; |
- fts3EvalNextRow(pCsr, pExpr, &rc); |
- if( rc!=SQLITE_OK ) return rc; |
- iDocid = pExpr->iDocid; |
- pIter = pPhrase->doclist.pList; |
+ if( bTreeEof ){ |
+ while( rc==SQLITE_OK && !pNear->bEof ){ |
+ fts3EvalNextRow(pCsr, pNear, &rc); |
+ } |
} |
+ if( rc!=SQLITE_OK ) return rc; |
- bEof = (pPhrase->doclist.nAll==0); |
- assert( bDescDoclist==0 || bDescDoclist==1 ); |
- assert( pCsr->bDesc==0 || pCsr->bDesc==1 ); |
- |
- if( bEof==0 ){ |
+ bMatch = 1; |
+ for(p=pNear; p; p=p->pLeft){ |
+ u8 bEof = 0; |
+ Fts3Expr *pTest = p; |
+ Fts3Phrase *pPh; |
+ assert( pTest->eType==FTSQUERY_NEAR || pTest->eType==FTSQUERY_PHRASE ); |
+ if( pTest->eType==FTSQUERY_NEAR ) pTest = pTest->pRight; |
+ assert( pTest->eType==FTSQUERY_PHRASE ); |
+ pPh = pTest->pPhrase; |
+ |
+ pIter = pPh->pOrPoslist; |
+ iDocid = pPh->iOrDocid; |
if( pCsr->bDesc==bDescDoclist ){ |
- int dummy; |
- if( pNear->bEof ){ |
- /* This expression is already at EOF. So position it to point to the |
- ** last entry in the doclist at pPhrase->doclist.aAll[]. Variable |
- ** iDocid is already set for this entry, so all that is required is |
- ** to set pIter to point to the first byte of the last position-list |
- ** in the doclist. |
- ** |
- ** It would also be correct to set pIter and iDocid to zero. In |
- ** this case, the first call to sqltie3Fts4DoclistPrev() below |
- ** would also move the iterator to point to the last entry in the |
- ** doclist. However, this is expensive, as to do so it has to |
- ** iterate through the entire doclist from start to finish (since |
- ** it does not know the docid for the last entry). */ |
- pIter = &pPhrase->doclist.aAll[pPhrase->doclist.nAll-1]; |
- fts3ReversePoslist(pPhrase->doclist.aAll, &pIter); |
- } |
- while( (pIter==0 || DOCID_CMP(iDocid, pCsr->iPrevId)>0 ) && bEof==0 ){ |
- sqlite3Fts3DoclistPrev( |
- bDescDoclist, pPhrase->doclist.aAll, pPhrase->doclist.nAll, |
- &pIter, &iDocid, &dummy, &bEof |
- ); |
- } |
- }else{ |
- if( pNear->bEof ){ |
- pIter = 0; |
- iDocid = 0; |
- } |
+ bEof = !pPh->doclist.nAll || |
+ (pIter >= (pPh->doclist.aAll + pPh->doclist.nAll)); |
while( (pIter==0 || DOCID_CMP(iDocid, pCsr->iPrevId)<0 ) && bEof==0 ){ |
sqlite3Fts3DoclistNext( |
- bDescDoclist, pPhrase->doclist.aAll, pPhrase->doclist.nAll, |
+ bDescDoclist, pPh->doclist.aAll, pPh->doclist.nAll, |
&pIter, &iDocid, &bEof |
); |
} |
+ }else{ |
+ bEof = !pPh->doclist.nAll || (pIter && pIter<=pPh->doclist.aAll); |
+ while( (pIter==0 || DOCID_CMP(iDocid, pCsr->iPrevId)>0 ) && bEof==0 ){ |
+ int dummy; |
+ sqlite3Fts3DoclistPrev( |
+ bDescDoclist, pPh->doclist.aAll, pPh->doclist.nAll, |
+ &pIter, &iDocid, &dummy, &bEof |
+ ); |
+ } |
} |
+ pPh->pOrPoslist = pIter; |
+ pPh->iOrDocid = iDocid; |
+ if( bEof || iDocid!=pCsr->iPrevId ) bMatch = 0; |
} |
- if( bEof || iDocid!=pCsr->iPrevId ) pIter = 0; |
+ if( bMatch ){ |
+ pIter = pPhrase->pOrPoslist; |
+ }else{ |
+ pIter = 0; |
+ } |
} |
if( pIter==0 ) return SQLITE_OK; |
@@ -5756,10 +5877,13 @@ int sqlite3Fts3EvalPhrasePoslist( |
} |
while( iThis<iCol ){ |
fts3ColumnlistCopy(0, &pIter); |
- if( *pIter==0x00 ) return 0; |
+ if( *pIter==0x00 ) return SQLITE_OK; |
pIter++; |
pIter += fts3GetVarint32(pIter, &iThis); |
} |
+ if( *pIter==0x00 ){ |
+ pIter = 0; |
+ } |
*ppOut = ((iCol==iThis)?pIter:0); |
return SQLITE_OK; |