diff --git a/ext/fts5/fts5.c b/ext/fts5/fts5.c index 368a0f88c9..fb3c0d7197 100644 --- a/ext/fts5/fts5.c +++ b/ext/fts5/fts5.c @@ -165,6 +165,9 @@ struct Fts5Cursor { Fts5Auxiliary *pAux; /* Currently executing extension function */ Fts5Auxdata *pAuxdata; /* First in linked list of saved aux-data */ int *aColumnSize; /* Values for xColumnSize() */ + + int nInstCount; /* Number of phrase instances */ + int *aInst; /* 3 integers per phrase instance */ }; /* @@ -488,6 +491,18 @@ static int fts5StmtType(int idxNum){ return FTS5_STMT_LOOKUP; } +/* +** This function is called after the cursor passed as the only argument +** is moved to point at a different row. It clears all cached data +** specific to the previous row stored by the cursor object. +*/ +static void fts5CsrNewrow(Fts5Cursor *pCsr){ + CsrFlagSet(pCsr, FTS5CSR_REQUIRE_CONTENT | FTS5CSR_REQUIRE_DOCSIZE ); + sqlite3_free(pCsr->aInst); + pCsr->aInst = 0; + pCsr->nInstCount = 0; +} + /* ** Close the cursor. For additional information see the documentation ** on the xClose method of the virtual table interface. @@ -499,6 +514,7 @@ static int fts5CloseMethod(sqlite3_vtab_cursor *pCursor){ Fts5Auxdata *pData; Fts5Auxdata *pNext; + fts5CsrNewrow(pCsr); if( pCsr->pStmt ){ int eStmt = fts5StmtType(pCsr->idxNum); sqlite3Fts5StorageStmtRelease(pTab->pStorage, eStmt, pCsr->pStmt); @@ -557,7 +573,7 @@ static int fts5SorterNext(Fts5Cursor *pCsr){ pSorter->aIdx[i] = &aBlob[nBlob] - a; pSorter->aPoslist = a; - CsrFlagSet(pCsr, FTS5CSR_REQUIRE_CONTENT | FTS5CSR_REQUIRE_DOCSIZE ); + fts5CsrNewrow(pCsr); } return rc; @@ -583,7 +599,7 @@ static int fts5NextMethod(sqlite3_vtab_cursor *pCursor){ if( sqlite3Fts5ExprEof(pCsr->pExpr) ){ CsrFlagSet(pCsr, FTS5CSR_EOF); } - CsrFlagSet(pCsr, FTS5CSR_REQUIRE_CONTENT | FTS5CSR_REQUIRE_DOCSIZE ); + fts5CsrNewrow(pCsr); break; case FTS5_PLAN_SPECIAL: { @@ -666,7 +682,7 @@ static int fts5CursorFirst(Fts5Table *pTab, Fts5Cursor *pCsr, int bAsc){ if( sqlite3Fts5ExprEof(pCsr->pExpr) ){ CsrFlagSet(pCsr, FTS5CSR_EOF); } - CsrFlagSet(pCsr, FTS5CSR_REQUIRE_CONTENT | FTS5CSR_REQUIRE_DOCSIZE ); + fts5CsrNewrow(pCsr); return rc; } @@ -1044,6 +1060,104 @@ static int fts5ApiPhraseSize(Fts5Context *pCtx, int iPhrase){ return sqlite3Fts5ExprPhraseSize(pCsr->pExpr, iPhrase); } +static int fts5CsrPoslist(Fts5Cursor *pCsr, int iPhrase, const u8 **pa){ + int n; + if( pCsr->pSorter ){ + Fts5Sorter *pSorter = pCsr->pSorter; + int i1 = (iPhrase==0 ? 0 : pSorter->aIdx[iPhrase-1]); + n = pSorter->aIdx[iPhrase] - i1; + *pa = &pSorter->aPoslist[i1]; + }else{ + n = sqlite3Fts5ExprPoslist(pCsr->pExpr, iPhrase, pa); + } + return n; +} + +/* +** Ensure that the Fts5Cursor.nInstCount and aInst[] variables are populated +** correctly for the current view. Return SQLITE_OK if successful, or an +** SQLite error code otherwise. +*/ +static int fts5CacheInstArray(Fts5Cursor *pCsr){ + int rc = SQLITE_OK; + if( pCsr->aInst==0 ){ + Fts5PoslistReader *aIter; /* One iterator for each phrase */ + int nIter; /* Number of iterators/phrases */ + int nByte; + + nIter = sqlite3Fts5ExprPhraseCount(pCsr->pExpr); + nByte = sizeof(Fts5PoslistReader) * nIter; + aIter = (Fts5PoslistReader*)sqlite3Fts5MallocZero(&rc, nByte); + if( aIter ){ + Fts5Buffer buf = {0, 0, 0}; /* Build up aInst[] here */ + int nInst; /* Number instances seen so far */ + int i; + + /* Initialize all iterators */ + for(i=0; iaInst = (int*)buf.p; + pCsr->nInstCount = nInst; + sqlite3_free(aIter); + } + } + return rc; +} + +static int fts5ApiInstCount(Fts5Context *pCtx, int *pnInst){ + Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; + int rc; + if( SQLITE_OK==(rc = fts5CacheInstArray(pCsr)) ){ + *pnInst = pCsr->nInstCount; + } + return rc; +} + +static int fts5ApiInst( + Fts5Context *pCtx, + int iIdx, + int *piPhrase, + int *piCol, + int *piOff +){ + Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; + int rc; + if( SQLITE_OK==(rc = fts5CacheInstArray(pCsr)) ){ + if( iIdx<0 || iIdx>=pCsr->nInstCount ){ + rc = SQLITE_RANGE; + }else{ + *piPhrase = pCsr->aInst[iIdx*3]; + *piCol = pCsr->aInst[iIdx*3 + 1]; + *piOff = pCsr->aInst[iIdx*3 + 2]; + } + } + return rc; +} + static sqlite3_int64 fts5ApiRowid(Fts5Context *pCtx){ return fts5CursorRowid((Fts5Cursor*)pCtx); } @@ -1088,14 +1202,7 @@ static int fts5ApiPoslist( ){ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; const u8 *a; int n; /* Poslist for phrase iPhrase */ - if( pCsr->pSorter ){ - Fts5Sorter *pSorter = pCsr->pSorter; - int i1 = (iPhrase==0 ? 0 : pSorter->aIdx[iPhrase-1]); - n = pSorter->aIdx[iPhrase] - i1; - a = &pSorter->aPoslist[i1]; - }else{ - n = sqlite3Fts5ExprPoslist(pCsr->pExpr, iPhrase, &a); - } + n = fts5CsrPoslist(pCsr, iPhrase, &a); return sqlite3Fts5PoslistNext64(a, n, pi, piPos); } @@ -1162,6 +1269,8 @@ static const Fts5ExtensionApi sFts5Api = { fts5ApiTokenize, fts5ApiPhraseCount, fts5ApiPhraseSize, + fts5ApiInstCount, + fts5ApiInst, fts5ApiRowid, fts5ApiColumnText, fts5ApiColumnSize, diff --git a/ext/fts5/fts5.h b/ext/fts5/fts5.h index 248459ac8a..71db9577c9 100644 --- a/ext/fts5/fts5.h +++ b/ext/fts5/fts5.h @@ -70,13 +70,42 @@ typedef void (*fts5_extension_function)( ** Returns the number of tokens in phrase iPhrase of the query. Phrases ** are numbered starting from zero. ** +** xInstCount: +** Set *pnInst to the total number of occurrences of all phrases within +** the query within the current row. Return SQLITE_OK if successful, or +** an error code (i.e. SQLITE_NOMEM) if an error occurs. +** +** xInst: +** Query for the details of phrase match iIdx within the current row. +** Phrase matches are numbered starting from zero, so the iIdx argument +** should be greater than or equal to zero and smaller than the value +** output by xInstCount(). +** +** Returns SQLITE_OK if successful, or an error code (i.e. SQLITE_NOMEM) +** if an error occurs. +** ** xRowid: ** Returns the rowid of the current row. ** ** xPoslist: -** Iterate through instances of phrase iPhrase in the current row. +** Iterate through phrase instances in the current row. If the iPhrase +** argument is 0 or greater, then only instances of phrase iPhrase are +** visited. If it is less than 0, instances of all phrases are visited. +** +** At EOF, -1 is returned and output variable iPos set to -1. +** +** +** sqlite3_int64 iPos; +** int iPhrase; +** int ii = 0; +** +** while( (iPhrase = pFts->xPoslist(pFts, -1, &ii, &iPos) >= 0 ){ +** int iCol = FTS5_POS2COLUMN(iPos); +** int iOff = FTS5_POS2OFFSET(iPos); +** // An instance of phrase iPhrase at offset iOff of column iCol. +** } +** ** -** At EOF, a non-zero value is returned and output variable iPos set to -1. ** ** xTokenize: ** Tokenize text using the tokenizer belonging to the FTS5 table. @@ -160,6 +189,9 @@ struct Fts5ExtensionApi { int (*xPhraseCount)(Fts5Context*); int (*xPhraseSize)(Fts5Context*, int iPhrase); + int (*xInstCount)(Fts5Context*, int *pnInst); + int (*xInst)(Fts5Context*, int iIdx, int *piPhrase, int *piCol, int *piOff); + sqlite3_int64 (*xRowid)(Fts5Context*); int (*xColumnText)(Fts5Context*, int iCol, const char **pz, int *pn); int (*xColumnSize)(Fts5Context*, int iCol, int *pnToken); diff --git a/ext/fts5/fts5Int.h b/ext/fts5/fts5Int.h index 3808d31617..999777fcdf 100644 --- a/ext/fts5/fts5Int.h +++ b/ext/fts5/fts5Int.h @@ -301,6 +301,9 @@ void sqlite3Fts5IndexAutomerge(Fts5Index *p, int nMerge); */ int sqlite3Fts5IndexReads(Fts5Index *p); +/* Malloc utility */ +void *sqlite3Fts5MallocZero(int *pRc, int nByte); + /* ** End of interface to code in fts5_index.c. **************************************************************************/ diff --git a/ext/fts5/fts5_aux.c b/ext/fts5/fts5_aux.c index 186b43c166..a039b7b536 100644 --- a/ext/fts5/fts5_aux.c +++ b/ext/fts5/fts5_aux.c @@ -14,6 +14,138 @@ #include "fts5Int.h" #include +/************************************************************************* +** Start of highlight() implementation. +*/ +typedef struct HighlightContext HighlightContext; +struct HighlightContext { + const Fts5ExtensionApi *pApi; /* API offered by current FTS version */ + Fts5Context *pFts; /* First arg to pass to pApi functions */ + int iInst; /* Current phrase instance index */ + int iStart; /* First token of current phrase */ + int iEnd; /* Last token of current phrase */ + + const char *zOpen; /* Opening highlight */ + const char *zClose; /* Closing highlight */ + int iCol; /* Column to read from */ + + const char *zIn; /* Input text */ + int nIn; /* Size of input text in bytes */ + int iOff; /* Current offset within zIn[] */ + char *zOut; /* Output value */ +}; + +static int fts5HighlightAppend(HighlightContext *p, const char *z, int n){ + if( n<0 ) n = strlen(z); + p->zOut = sqlite3_mprintf("%z%.*s", p->zOut, n, z); + if( p->zOut==0 ) return SQLITE_NOMEM; + return SQLITE_OK; +} + +static int fts5HighlightCb( + void *pContext, /* Pointer to HighlightContext object */ + const char *pToken, /* Buffer containing token */ + int nToken, /* Size of token in bytes */ + int iStart, /* Start offset of token */ + int iEnd, /* End offset of token */ + int iPos /* Position offset of token */ +){ + HighlightContext *p = (HighlightContext*)pContext; + int rc = SQLITE_OK; + + if( iPos==p->iStart ){ + rc = fts5HighlightAppend(p, &p->zIn[p->iOff], iStart - p->iOff); + p->iOff = iStart; + if( rc==SQLITE_OK ){ + rc = fts5HighlightAppend(p, p->zOpen, -1); + } + } + + if( rc==SQLITE_OK ){ + rc = fts5HighlightAppend(p, &p->zIn[p->iOff], iEnd - p->iOff); + p->iOff = iEnd; + } + + if( rc==SQLITE_OK && iPos==p->iEnd ){ + int bClose = 1; + do{ + int iP, iPCol, iOff; + rc = p->pApi->xInst(p->pFts, ++p->iInst, &iP, &iPCol, &iOff); + if( rc==SQLITE_RANGE || iPCol!=p->iCol ){ + p->iStart = -1; + p->iEnd = -1; + rc = SQLITE_OK; + }else{ + iEnd = iOff - 1 + p->pApi->xPhraseSize(p->pFts, iP); + if( iEnd<=p->iEnd ) continue; + if( iOff<=p->iEnd ) bClose = 0; + p->iStart = iOff; + p->iEnd = iEnd; + } + }while( 0 ); + + if( rc==SQLITE_OK && bClose ){ + rc = fts5HighlightAppend(p, p->zClose, -1); + } + } + + return rc; +} + +static void fts5HighlightFunction( + const Fts5ExtensionApi *pApi, /* API offered by current FTS version */ + Fts5Context *pFts, /* First arg to pass to pApi functions */ + sqlite3_context *pCtx, /* Context for returning result/error */ + int nVal, /* Number of values in apVal[] array */ + sqlite3_value **apVal /* Array of trailing arguments */ +){ + HighlightContext ctx; + int rc; + + if( nVal!=3 ){ + const char *zErr = "wrong number of arguments to function highlight()"; + sqlite3_result_error(pCtx, zErr, -1); + return; + } + memset(&ctx, 0, sizeof(HighlightContext)); + ctx.iCol = sqlite3_value_int(apVal[0]); + ctx.zOpen = (const char*)sqlite3_value_text(apVal[1]); + ctx.zClose = (const char*)sqlite3_value_text(apVal[2]); + rc = pApi->xColumnText(pFts, ctx.iCol, &ctx.zIn, &ctx.nIn); + ctx.pApi = pApi; + ctx.pFts = pFts; + + /* Find the first phrase instance in the right column. */ + ctx.iStart = -1; + ctx.iEnd = -1; + while( rc==SQLITE_OK ){ + int iP, iPCol, iOff; + rc = pApi->xInst(pFts, ctx.iInst, &iP, &iPCol, &iOff); + if( rc==SQLITE_OK && iPCol==ctx.iCol ){ + ctx.iStart = iOff; + ctx.iEnd = iOff - 1 + pApi->xPhraseSize(pFts, iP); + break; + } + ctx.iInst++; + } + + if( rc==SQLITE_OK || rc==SQLITE_RANGE ){ + rc = pApi->xTokenize(pFts, ctx.zIn, ctx.nIn, (void*)&ctx, fts5HighlightCb); + } + if( rc==SQLITE_OK ){ + rc = fts5HighlightAppend(&ctx, &ctx.zIn[ctx.iOff], ctx.nIn - ctx.iOff); + } + + if( rc==SQLITE_OK ){ + sqlite3_result_text(pCtx, (const char*)ctx.zOut, -1, SQLITE_TRANSIENT); + }else{ + sqlite3_result_error_code(pCtx, rc); + } + sqlite3_free(ctx.zOut); +} +/* +**************************************************************************/ + typedef struct SnipPhrase SnipPhrase; typedef struct SnipIter SnipIter; typedef struct SnippetCtx SnippetCtx; @@ -796,6 +928,22 @@ static void fts5TestFunction( } } + /* + ** xInst() + */ + if( zReq==0 ) sqlite3Fts5BufferAppendPrintf(&rc, &s, " inst "); + if( 0==zReq || 0==sqlite3_stricmp(zReq, "inst") ){ + int nInst; + rc = pApi->xInstCount(pFts, &nInst); + for(i=0; rc==SQLITE_OK && ixInst(pFts, i, &iPhrase, &iCol, &iOff); + sqlite3Fts5BufferAppendPrintf(&rc, &s, "%s%d.%d.%d", + (i==0 ? "" : " "), iPhrase, iCol, iOff + ); + } + } + /* ** xPhraseCount() */ @@ -966,6 +1114,7 @@ int sqlite3Fts5AuxInit(fts5_api *pApi){ { "bm25debug", (void*)1, fts5Bm25Function, 0 }, { "snippet", 0, fts5SnippetFunction, 0 }, { "fts5_test", 0, fts5TestFunction, 0 }, + { "highlight", 0, fts5HighlightFunction, 0 }, { "bm25", 0, fts5Bm25Function, 0 }, }; diff --git a/ext/fts5/fts5_index.c b/ext/fts5/fts5_index.c index 214cc13931..b6923a3cf4 100644 --- a/ext/fts5/fts5_index.c +++ b/ext/fts5/fts5_index.c @@ -692,7 +692,7 @@ static void *fts5IdxMalloc(Fts5Index *p, int nByte){ return pRet; } -static void *fts5MallocZero(int *pRc, int nByte){ +void *sqlite3Fts5MallocZero(int *pRc, int nByte){ void *pRet = 0; if( *pRc==SQLITE_OK ){ pRet = sqlite3_malloc(nByte); @@ -981,7 +981,7 @@ static int fts5StructureDecode( sizeof(Fts5Structure) + /* Main structure */ sizeof(Fts5StructureLevel) * (nLevel) /* aLevel[] array */ ); - pRet = (Fts5Structure*)fts5MallocZero(&rc, nByte); + pRet = (Fts5Structure*)sqlite3Fts5MallocZero(&rc, nByte); if( pRet ){ pRet->nLevel = nLevel; @@ -995,7 +995,7 @@ static int fts5StructureDecode( i += getVarint32(&pData[i], pLvl->nMerge); i += getVarint32(&pData[i], nTotal); assert( nTotal>=pLvl->nMerge ); - pLvl->aSeg = (Fts5StructureSegment*)fts5MallocZero(&rc, + pLvl->aSeg = (Fts5StructureSegment*)sqlite3Fts5MallocZero(&rc, nTotal * sizeof(Fts5StructureSegment) ); diff --git a/manifest b/manifest index 3c7f49ac42..7d776a97d4 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sthe\scustomization\sinterfaces\sso\sthat\sthey\smatch\sthe\sdocumentation. -D 2014-11-15T20:07:31.166 +C Add\sthe\sauxiliary\shighlight()\sfunction\sto\sfts5. +D 2014-11-24T16:24:33.456 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 cc3f0e4bac499c81d1311199f929dcad5e40ee8e -F ext/fts5/fts5.h a77cad780eec8f10850fdba0f44079a92561b790 -F ext/fts5/fts5Int.h a3c46f9dae13277de6fc3a6f8863d337ca660d6a -F ext/fts5/fts5_aux.c 6b0612e4312ca27264f7dacb0c97abc723a4b472 +F ext/fts5/fts5.c d4b9895c5dc11c20493b3a9f09f4a0cdb0bc1438 +F ext/fts5/fts5.h 72fc1e9995b1ddc254a487b9528614a83bd3dfb6 +F ext/fts5/fts5Int.h fd811979294410b10c1737392a9114510fc2a1be +F ext/fts5/fts5_aux.c 2e467bdd93f23f049824411b326f77b9326cb61a F ext/fts5/fts5_buffer.c 248c61ac9fec001602efc72a45704f3b8d367c00 F ext/fts5/fts5_config.c a292fe73864086e51e7974d842cc09f6379fbae0 F ext/fts5/fts5_expr.c d317be07d70223a6865444f17982570260b690a5 F ext/fts5/fts5_hash.c 63fa8379c5f2ac107d47c2b7d9ac04c95ef8a279 -F ext/fts5/fts5_index.c 3f4d84a1762e4284319739d4672b90b18b91060a +F ext/fts5/fts5_index.c 998c4aa0f003666afe85b6ff821476419ed245e9 F ext/fts5/fts5_storage.c 5913aa01a1dada1c5e1a39e4cbb44e84c5f7f350 F ext/fts5/fts5_tokenize.c 8360c0d1ae0d4696f3cc13f7c67a2db6011cdc5b F ext/fts5/fts5parse.y 777da8e5819f75c217982c79c29d014c293acac9 @@ -602,12 +602,13 @@ F test/fts5aa.test 16bf1dbb92d4d63c7c357b480b1a47309f654ad1 F test/fts5ab.test 657d6dc5ddc57bfea4af1bb85204d4f3539cd3e8 F test/fts5ac.test f38ceca8a43fa0ff86122bec72428a4067b17bc4 F test/fts5ad.test d29ff407c70df470c9a8fcbfe5bc80efd662f2c4 -F test/fts5ae.test d4141786d817e0198f89f8c66749af38359839a7 +F test/fts5ae.test a514ee09be90723ccc9736edaef900a5af1c121a F test/fts5af.test d24e3b0f879998ef5f60087272f8ab7b3a8fd4dc F test/fts5ag.test 1c6c188d1bdc41b2277db3f4ddfea7d90bf44ceb F test/fts5ah.test af9274cdb58a69780c7e57e61581990665ac0fb6 F test/fts5ai.test aa2b5fd0f8d2cf59ac0211111e63cbca3b40ed7d F test/fts5aj.test fe5c40216cac8072f29e454ee0540c7b89d17ccd +F test/fts5ak.test 2c930afe32bd15b39a2c416fabe9fc7a36e3042e F test/fts5ea.test afaf3497b43add578384dc1fd26b0342738abe87 F test/full.test 6b3c8fb43c6beab6b95438c1675374b95fab245d F test/func.test ae97561957aba6ca9e3a7b8a13aac41830d701ef @@ -1204,7 +1205,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 e240d467e60b7755486aae5e8b0824f7c741f852 -R 28baa98ae078d2f041a83a26b4550455 +P fba0b5fc7eead07a4853e78e02d788e7c714f6cd +R 0f9755027ecd37d1e64800649e8a07f5 U dan -Z 1c1b566687b968f39cda6c2d32a692b6 +Z 35a88e8d2ae30031588d0943e2aec6ce diff --git a/manifest.uuid b/manifest.uuid index ccae749918..374484385c 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -fba0b5fc7eead07a4853e78e02d788e7c714f6cd \ No newline at end of file +059092379f981eb919b500ce447006f9e645fc5a \ No newline at end of file diff --git a/test/fts5ae.test b/test/fts5ae.test index 32d75616ae..b770d00c5f 100644 --- a/test/fts5ae.test +++ b/test/fts5ae.test @@ -269,7 +269,7 @@ foreach {tn q res} { SELECT rowid FROM t8 WHERE t8 MATCH $q ORDER BY +rank DESC; } $res - do_execsql_test 8.3.$tn.3 { + do_execsql_test 8.2.$tn.3 { SELECT rowid FROM t8 WHERE t8 MATCH $q ORDER BY rank DESC; } $res } diff --git a/test/fts5ak.test b/test/fts5ak.test new file mode 100644 index 0000000000..4d5b22b030 --- /dev/null +++ b/test/fts5ak.test @@ -0,0 +1,111 @@ +# 2014 November 24 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#************************************************************************* +# This file implements regression tests for SQLite library. The +# focus of this script is testing the FTS5 module. +# +# Specifically, the auxiliary function "highlight". +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix fts5aj + +# If SQLITE_ENABLE_FTS5 is defined, omit this file. +ifcapable !fts5 { + finish_test + return +} + +do_execsql_test 1.1 { + CREATE VIRTUAL TABLE ft1 USING fts5(x); + INSERT INTO ft1 VALUES('i d d a g i b g d d'); + INSERT INTO ft1 VALUES('h d b j c c g a c a'); + INSERT INTO ft1 VALUES('e j a e f h b f h h'); + INSERT INTO ft1 VALUES('j f h d g h i b d f'); + INSERT INTO ft1 VALUES('d c j d c j b c g e'); + INSERT INTO ft1 VALUES('i a d e g j g d a a'); + INSERT INTO ft1 VALUES('j f c e d a h j d b'); + INSERT INTO ft1 VALUES('i c c f a d g h j e'); + INSERT INTO ft1 VALUES('i d i g c d c h b f'); + INSERT INTO ft1 VALUES('g d a e h a b c f j'); +} + +do_execsql_test 1.2 { + SELECT highlight(ft1, 0, '[', ']') FROM ft1 WHERE ft1 MATCH 'e'; +} { + {g d a [e] h a b c f j} + {i c c f a d g h j [e]} + {j f c [e] d a h j d b} + {i a d [e] g j g d a a} + {d c j d c j b c g [e]} + {[e] j a [e] f h b f h h} +} + +do_execsql_test 1.3 { + SELECT highlight(ft1, 0, '[', ']') FROM ft1 WHERE ft1 MATCH 'h + d'; +} { + {j f [h d] g h i b d f} + {[h d] b j c c g a c a} +} + +do_execsql_test 1.4 { + SELECT highlight(ft1, 0, '[', ']') FROM ft1 WHERE ft1 MATCH 'd + d'; +} { + {i [d d] a g i b g [d d]} +} + +do_execsql_test 1.5 { + SELECT highlight(ft1, 0, '[', ']') FROM ft1 WHERE ft1 MATCH 'e e e' +} { + {g d a [e] h a b c f j} + {i c c f a d g h j [e]} + {j f c [e] d a h j d b} + {i a d [e] g j g d a a} + {d c j d c j b c g [e]} + {[e] j a [e] f h b f h h} +} + +do_execsql_test 1.6 { + SELECT highlight(ft1, 0, '[', ']') FROM ft1 WHERE ft1 MATCH 'd + d d + d'; +} { + {i [d d] a g i b g [d d]} +} + +do_execsql_test 2.1 { + CREATE VIRTUAL TABLE ft2 USING fts5(x); + INSERT INTO ft2 VALUES('a b c d e f g h i j'); +} + +do_execsql_test 2.2 { + SELECT highlight(ft2, 0, '[', ']') FROM ft2 WHERE ft2 MATCH 'b+c+d c+d+e' +} {{a [b c d e] f g h i j}} + +do_execsql_test 2.3 { + SELECT highlight(ft2, 0, '[', ']') FROM ft2 WHERE ft2 MATCH 'b+c+d e+f+g' +} { + {a [b c d] [e f g] h i j} +} + +do_execsql_test 2.4 { + SELECT highlight(ft2, 0, '[', ']') FROM ft2 WHERE ft2 MATCH 'b+c+d c' +} { + {a [b c d] e f g h i j} +} + +do_execsql_test 2.5 { + SELECT highlight(ft2, 0, '[', ']') FROM ft2 WHERE ft2 MATCH 'b+c c+d+e' +} { + {a [b c d e] f g h i j} +} + + +finish_test +