| Index: third_party/sqlite/sqlite-src-3100200/ext/fts3/fts3.c
|
| diff --git a/third_party/sqlite/sqlite-src-3080704/ext/fts3/fts3.c b/third_party/sqlite/sqlite-src-3100200/ext/fts3/fts3.c
|
| similarity index 95%
|
| copy from third_party/sqlite/sqlite-src-3080704/ext/fts3/fts3.c
|
| copy to third_party/sqlite/sqlite-src-3100200/ext/fts3/fts3.c
|
| index 2b93c6271576c8416d109120803c8556a51057f9..748faefec5a2fe91e2bb1d404b5282a00c9404f1 100644
|
| --- a/third_party/sqlite/sqlite-src-3080704/ext/fts3/fts3.c
|
| +++ b/third_party/sqlite/sqlite-src-3100200/ext/fts3/fts3.c
|
| @@ -313,6 +313,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.
|
| @@ -422,7 +429,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;
|
| @@ -502,6 +509,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.
|
| @@ -910,11 +928,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;
|
| @@ -957,7 +980,6 @@ static int fts3PrefixParameter(
|
|
|
| aIndex = sqlite3_malloc(sizeof(struct Fts3Index) * nIndex);
|
| *apIndex = aIndex;
|
| - *pnIndex = nIndex;
|
| if( !aIndex ){
|
| return SQLITE_NOMEM;
|
| }
|
| @@ -967,13 +989,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;
|
| }
|
|
|
| @@ -1008,7 +1037,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 */
|
| @@ -1019,6 +1049,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);
|
|
|
| @@ -1097,7 +1130,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: */
|
| @@ -1185,13 +1218,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;
|
| @@ -1219,7 +1252,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');
|
| @@ -1270,7 +1303,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. */
|
| @@ -1305,7 +1338,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;
|
|
|
| @@ -1387,7 +1420,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;
|
| }
|
| }
|
| @@ -1395,7 +1428,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);
|
| @@ -1484,6 +1517,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:
|
| @@ -1574,6 +1620,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++;
|
| @@ -1642,7 +1691,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;
|
| @@ -1853,7 +1902,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 );
|
| @@ -1864,7 +1913,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);
|
| @@ -2491,26 +2540,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);
|
|
|
| @@ -2539,6 +2595,12 @@ static void fts3DoclistPhraseMerge(
|
| }
|
|
|
| *pnRight = (int)(p - aOut);
|
| + if( bDescDoclist ){
|
| + sqlite3_free(aRight);
|
| + *paRight = aOut;
|
| + }
|
| +
|
| + return SQLITE_OK;
|
| }
|
|
|
| /*
|
| @@ -2663,8 +2725,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);
|
| @@ -2761,7 +2837,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);
|
| }
|
| @@ -3086,7 +3162,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;
|
| @@ -3116,7 +3192,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));
|
|
|
| @@ -3164,10 +3240,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);
|
| @@ -3403,11 +3486,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;
|
| }
|
| @@ -3478,6 +3581,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);
|
| }
|
| @@ -3913,14 +4018,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 ){
|
| @@ -3959,13 +4067,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;
|
| }
|
|
|
| /*
|
| @@ -3991,7 +4102,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 );
|
| @@ -4136,7 +4247,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
|
| @@ -4256,6 +4366,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{
|
| @@ -4533,12 +4644,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);
|
| @@ -4794,8 +4907,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
|
| );
|
| @@ -4973,7 +5090,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(
|
| @@ -5020,6 +5137,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;
|
| }
|
| @@ -5177,7 +5310,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.
|
| @@ -5298,7 +5431,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 ){
|
| @@ -5345,7 +5478,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
|
| @@ -5392,6 +5525,7 @@ static void fts3EvalRestart(
|
| }
|
| pPhrase->doclist.pNextDocid = 0;
|
| pPhrase->doclist.iDocid = 0;
|
| + pPhrase->pOrPoslist = 0;
|
| }
|
|
|
| pExpr->iDocid = 0;
|
| @@ -5505,7 +5639,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 ){
|
| @@ -5530,7 +5664,6 @@ static int fts3EvalGatherStats(
|
| fts3EvalNextRow(pCsr, pRoot, &rc);
|
| assert( pRoot->bEof==0 );
|
| }while( pRoot->iDocid!=iDocid && rc==SQLITE_OK );
|
| - fts3EvalTestDeferredAndNear(pCsr, &rc);
|
| }
|
| }
|
| return rc;
|
| @@ -5637,13 +5770,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
|
| @@ -5662,74 +5795,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;
|
|
|
| @@ -5741,10 +5862,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;
|
|
|