Fix issues with position lists and NEAR constraints.

FossilOrigin-Name: 16352d3654d5672cd0251db51dbe19f779373feb
This commit is contained in:
dan 2014-07-18 19:59:00 +00:00
parent c5b44f3d90
commit 30b650e2f4
7 changed files with 357 additions and 67 deletions

View File

@ -118,8 +118,7 @@ int sqlite3Fts5PoslistReaderNext(Fts5PoslistReader*);
typedef struct Fts5PoslistWriter Fts5PoslistWriter;
struct Fts5PoslistWriter {
int iCol;
int iOff;
i64 iPrev;
};
int sqlite3Fts5PoslistWriterAppend(Fts5Buffer*, Fts5PoslistWriter*, i64);
@ -130,6 +129,12 @@ int sqlite3Fts5PoslistNext(
int *piOff /* IN/OUT: Current token offset */
);
int sqlite3Fts5PoslistNext64(
const u8 *a, int n, /* Buffer containing poslist */
int *pi, /* IN/OUT: Offset within a[] */
i64 *piOff /* IN/OUT: Current offset */
);
/*
** End of interface to code in fts5_buffer.c.
**************************************************************************/

View File

@ -138,27 +138,40 @@ void sqlite3Fts5BufferSet(
sqlite3Fts5BufferAppendBlob(pRc, pBuf, nData, pData);
}
int sqlite3Fts5PoslistNext64(
const u8 *a, int n, /* Buffer containing poslist */
int *pi, /* IN/OUT: Offset within a[] */
i64 *piOff /* IN/OUT: Current offset */
){
int i = *pi;
if( i>=n ){
/* EOF */
return 1;
}else{
i64 iOff = *piOff;
int iVal;
i += getVarint32(&a[i], iVal);
if( iVal==1 ){
i += getVarint32(&a[i], iVal);
iOff = ((i64)iVal) << 32;
i += getVarint32(&a[i], iVal);
}
*piOff = iOff + (iVal-2);
*pi = i;
return 0;
}
}
/*
** Advance the iterator object passed as the only argument. Return true
** if the iterator reaches EOF, or false otherwise.
*/
int sqlite3Fts5PoslistReaderNext(Fts5PoslistReader *pIter){
if( pIter->i>=pIter->n ){
if( sqlite3Fts5PoslistNext64(pIter->a, pIter->n, &pIter->i, &pIter->iPos)
|| (pIter->iCol>=0 && (pIter->iPos >> 32) > pIter->iCol)
){
pIter->bEof = 1;
}else{
int iVal;
pIter->i += getVarint32(&pIter->a[pIter->i], iVal);
if( iVal==1 ){
pIter->i += getVarint32(&pIter->a[pIter->i], iVal);
if( pIter->iCol>=0 && iVal>pIter->iCol ){
pIter->bEof = 1;
}else{
pIter->iPos = ((u64)iVal << 32);
pIter->i += getVarint32(&pIter->a[pIter->i], iVal);
}
}
pIter->iPos += (iVal-2);
}
return pIter->bEof;
}
@ -183,18 +196,15 @@ int sqlite3Fts5PoslistWriterAppend(
Fts5PoslistWriter *pWriter,
i64 iPos
){
static const i64 colmask = ((i64)(0x7FFFFFFF)) << 32;
int rc = SQLITE_OK;
int iCol = (int)(iPos >> 32);
int iOff = (iPos & 0x7FFFFFFF);
if( iCol!=pWriter->iCol ){
if( (iPos & colmask) != (pWriter->iPrev & colmask) ){
fts5BufferAppendVarint(&rc, pBuf, 1);
fts5BufferAppendVarint(&rc, pBuf, iCol);
pWriter->iCol = iCol;
pWriter->iOff = 0;
fts5BufferAppendVarint(&rc, pBuf, (iPos >> 32));
pWriter->iPrev = (iPos & colmask);
}
fts5BufferAppendVarint(&rc, pBuf, (iOff - pWriter->iOff) + 2);
fts5BufferAppendVarint(&rc, pBuf, (iPos - pWriter->iPrev) + 2);
pWriter->iPrev = iPos;
return rc;
}

View File

@ -52,7 +52,7 @@ struct Fts5ExprNode {
Fts5ExprNode *pRight; /* Right hand child node */
Fts5ExprNearset *pNear; /* For FTS5_STRING - cluster of phrases */
int bEof; /* True at EOF */
i64 iRowid;
i64 iRowid; /* Current rowid */
};
/*
@ -70,6 +70,7 @@ struct Fts5ExprTerm {
** within a document for it to match.
*/
struct Fts5ExprPhrase {
Fts5ExprNode *pNode; /* FTS5_STRING node this phrase is part of */
Fts5Buffer poslist; /* Current position list */
int nTerm; /* Number of entries in aTerm[] */
Fts5ExprTerm aTerm[0]; /* Terms that make up this phrase */
@ -266,7 +267,7 @@ static int fts5ExprPhraseIsMatch(
Fts5ExprPhrase *pPhrase, /* Phrase object to initialize */
int *pbMatch /* OUT: Set to true if really a match */
){
Fts5PoslistWriter writer = {0, 0};
Fts5PoslistWriter writer = {0};
Fts5PoslistReader aStatic[4];
Fts5PoslistReader *aIter = aStatic;
int i;
@ -322,6 +323,46 @@ static int fts5ExprPhraseIsMatch(
return rc;
}
typedef struct Fts5LookaheadReader Fts5LookaheadReader;
struct Fts5LookaheadReader {
const u8 *a; /* Buffer containing position list */
int n; /* Size of buffer a[] in bytes */
int i; /* Current offset in position list */
i64 iPos; /* Current position */
i64 iLookahead; /* Next position */
};
#define FTS5_LOOKAHEAD_EOF (((i64)1) << 62)
static int fts5LookaheadReaderNext(Fts5LookaheadReader *p){
p->iPos = p->iLookahead;
if( sqlite3Fts5PoslistNext64(p->a, p->n, &p->i, &p->iLookahead) ){
p->iLookahead = FTS5_LOOKAHEAD_EOF;
}
return (p->iPos==FTS5_LOOKAHEAD_EOF);
}
static int fts5LookaheadReaderInit(
const u8 *a, int n, /* Buffer to read position list from */
Fts5LookaheadReader *p /* Iterator object to initialize */
){
memset(p, 0, sizeof(Fts5LookaheadReader));
p->a = a;
p->n = n;
fts5LookaheadReaderNext(p);
return fts5LookaheadReaderNext(p);
}
static int fts5LookaheadReaderEof(Fts5LookaheadReader *p){
return (p->iPos==FTS5_LOOKAHEAD_EOF);
}
typedef struct Fts5NearTrimmer Fts5NearTrimmer;
struct Fts5NearTrimmer {
Fts5LookaheadReader reader; /* Input iterator */
Fts5PoslistWriter writer; /* Writer context */
Fts5Buffer *pOut; /* Output poslist */
};
/*
** The near-set object passed as the first argument contains more than
@ -340,8 +381,11 @@ static int fts5ExprPhraseIsMatch(
** a set of intances that collectively matches the NEAR constraint.
*/
static int fts5ExprNearIsMatch(Fts5ExprNearset *pNear, int *pbMatch){
Fts5PoslistReader aStatic[4];
Fts5PoslistReader *aIter = aStatic;
Fts5NearTrimmer aStatic[4];
Fts5NearTrimmer *a = aStatic;
Fts5ExprPhrase **apPhrase = pNear->apPhrase;
int i;
int rc = SQLITE_OK;
int bMatch;
@ -352,36 +396,75 @@ static int fts5ExprNearIsMatch(Fts5ExprNearset *pNear, int *pbMatch){
/* If the aStatic[] array is not large enough, allocate a large array
** using sqlite3_malloc(). This approach could be improved upon. */
if( pNear->nPhrase>(sizeof(aStatic) / sizeof(aStatic[0])) ){
int nByte = sizeof(Fts5PoslistReader) * pNear->nPhrase;
aIter = (Fts5PoslistReader*)sqlite3_malloc(nByte);
if( !aIter ) return SQLITE_NOMEM;
int nByte = sizeof(Fts5LookaheadReader) * pNear->nPhrase;
a = (Fts5NearTrimmer*)sqlite3_malloc(nByte);
if( !a ) return SQLITE_NOMEM;
memset(a, 0, nByte);
}else{
memset(aStatic, 0, sizeof(aStatic));
}
/* Initialize a term iterator for each phrase */
/* Initialize a lookahead iterator for each phrase. After passing the
** buffer and buffer size to the lookaside-reader init function, zero
** the phrase poslist buffer. The new poslist for the phrase (containing
** the same entries as the original with some entries removed on account
** of the NEAR constraint) is written over the original even as it is
** being read. This is safe as the entries for the new poslist are a
** subset of the old, so it is not possible for data yet to be read to
** be overwritten. */
for(i=0; i<pNear->nPhrase; i++){
Fts5Buffer *pPoslist = &pNear->apPhrase[i]->poslist;
sqlite3Fts5PoslistReaderInit(-1, pPoslist->p, pPoslist->n, &aIter[i]);
Fts5Buffer *pPoslist = &apPhrase[i]->poslist;
fts5LookaheadReaderInit(pPoslist->p, pPoslist->n, &a[i].reader);
pPoslist->n = 0;
a[i].pOut = pPoslist;
}
iMax = aIter[0].iPos;
do {
bMatch = 1;
for(i=0; i<pNear->nPhrase; i++){
Fts5PoslistReader *pPos = &aIter[i];
i64 iMin = iMax - pNear->apPhrase[i]->nTerm - pNear->nNear;
if( pPos->iPos<iMin || pPos->iPos>iMax ){
bMatch = 0;
while( pPos->iPos<iMin ){
if( sqlite3Fts5PoslistReaderNext(pPos) ) goto ismatch_out;
while( 1 ){
int iAdv;
i64 iMin;
i64 iMax;
/* This block advances the phrase iterators until they point to a set of
** entries that together comprise a match. */
iMax = a[0].reader.iPos;
do {
bMatch = 1;
for(i=0; i<pNear->nPhrase; i++){
Fts5LookaheadReader *pPos = &a[i].reader;
iMin = iMax - pNear->apPhrase[i]->nTerm - pNear->nNear;
if( pPos->iPos<iMin || pPos->iPos>iMax ){
bMatch = 0;
while( pPos->iPos<iMin ){
if( fts5LookaheadReaderNext(pPos) ) goto ismatch_out;
}
if( pPos->iPos>iMax ) iMax = pPos->iPos;
}
if( pPos->iPos>iMax ) iMax = pPos->iPos;
}
}while( bMatch==0 );
/* Add an entry to each output position list */
for(i=0; i<pNear->nPhrase; i++){
i64 iPos = a[i].reader.iPos;
Fts5PoslistWriter *pWriter = &a[i].writer;
if( a[i].pOut->n==0 || iPos!=pWriter->iPrev ){
sqlite3Fts5PoslistWriterAppend(a[i].pOut, pWriter, iPos);
}
}
}while( bMatch==0 );
iAdv = 0;
iMin = a[0].reader.iLookahead;
for(i=0; i<pNear->nPhrase; i++){
if( a[i].reader.iLookahead < iMin ){
iMin = a[i].reader.iLookahead;
iAdv = i;
}
}
if( fts5LookaheadReaderNext(&a[iAdv].reader) ) goto ismatch_out;
}
ismatch_out:
*pbMatch = bMatch;
if( aIter!=aStatic ) sqlite3_free(aIter);
*pbMatch = (a[0].pOut->n>0);
if( a!=aStatic ) sqlite3_free(a);
return rc;
}
@ -519,7 +602,7 @@ static int fts5ExprNearNextMatch(
while( 1 ){
int i;
/* Advance the iterators until they are a match */
/* Advance the iterators until they all point to the same rowid */
rc = fts5ExprNearNextRowidMatch(pExpr, pNode);
if( pNode->bEof || rc!=SQLITE_OK ) break;
@ -545,6 +628,8 @@ static int fts5ExprNearNextMatch(
if( rc!=SQLITE_OK || bMatch ) break;
}
/* If control flows to here, then the current rowid is not a match.
** Advance all term iterators in all phrases to the next rowid. */
rc = fts5ExprNearAdvanceAll(pExpr, pNear, &pNode->bEof);
if( pNode->bEof || rc!=SQLITE_OK ) break;
}
@ -661,6 +746,14 @@ static int fts5ExprNodeNext(Fts5Expr *pExpr, Fts5ExprNode *pNode){
return rc;
}
static void fts5ExprSetEof(Fts5ExprNode *pNode){
if( pNode ){
pNode->bEof = 1;
fts5ExprSetEof(pNode->pLeft);
fts5ExprSetEof(pNode->pRight);
}
}
/*
**
*/
@ -688,7 +781,9 @@ static int fts5ExprNodeNextMatch(Fts5Expr *pExpr, Fts5ExprNode *pNode){
rc = fts5ExprNodeNext(pExpr, pAdv);
if( rc!=SQLITE_OK ) break;
}
pNode->bEof = p1->bEof || p2->bEof;
if( p1->bEof || p2->bEof ){
fts5ExprSetEof(pNode);
}
pNode->iRowid = p1->iRowid;
break;
}
@ -1090,6 +1185,12 @@ Fts5ExprNode *sqlite3Fts5ParseNode(
pRet->pLeft = pLeft;
pRet->pRight = pRight;
pRet->pNear = pNear;
if( eType==FTS5_STRING ){
int iPhrase;
for(iPhrase=0; iPhrase<pNear->nPhrase; iPhrase++){
pNear->apPhrase[iPhrase]->pNode = pRet;
}
}
}
}
@ -1398,7 +1499,8 @@ int sqlite3Fts5ExprPhraseSize(Fts5Expr *pExpr, int iPhrase){
int sqlite3Fts5ExprPoslist(Fts5Expr *pExpr, int iPhrase, const u8 **pa){
if( iPhrase>=0 && iPhrase<pExpr->nPhrase ){
Fts5ExprPhrase *pPhrase = pExpr->apPhrase[iPhrase];
if( sqlite3Fts5IterRowid(pPhrase->aTerm[0].pIter)==pExpr->pRoot->iRowid ){
Fts5ExprNode *pNode = pPhrase->pNode;
if( pNode->bEof==0 && pNode->iRowid==pExpr->pRoot->iRowid ){
*pa = pPhrase->poslist.p;
return pPhrase->poslist.n;
}

View File

@ -1,5 +1,5 @@
C Fix\sa\sproblem\swith\sposition\slist\sprocessing\sfor\sOR\squeries.
D 2014-07-17T15:14:07.541
C Fix\sissues\swith\sposition\slists\sand\sNEAR\sconstraints.
D 2014-07-18T19:59:00.547
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in b03432313a3aad96c706f8164fb9f5307eaf19f5
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@ -105,11 +105,11 @@ F ext/fts3/unicode/UnicodeData.txt cd07314edb62d49fde34debdaf92fa2aa69011e7
F ext/fts3/unicode/mkunicode.tcl dc6f268eb526710e2c6e496c372471d773d0c368
F ext/fts5/fts5.c 20bcb1e10756c72b550947236960edf96929ca2f
F ext/fts5/fts5.h cda3b9d73e6ffa6d0cd35b7da6b808bf3a1ada32
F ext/fts5/fts5Int.h 2d4c1e1ebdf18278776fcd8a64233ff3c04ea51f
F ext/fts5/fts5Int.h 6cf315d3999c14572012d676fa1baf4f4323587b
F ext/fts5/fts5_aux.c 27b082732fd76277fd7e9277f52903723d97f99b
F ext/fts5/fts5_buffer.c b7aa6cdf4a63642fcc12359cedc4be748ca400cc
F ext/fts5/fts5_buffer.c 71cf2016b2881e7aea39f952995eafa510d96cbd
F ext/fts5/fts5_config.c 94f1b4cb4de6a7cd5780c14adb0198e289df8cef
F ext/fts5/fts5_expr.c 52a1b47cfd30feb09c522392b1ba246eda7023f4
F ext/fts5/fts5_expr.c 288b3e016253eab69ea8cefbff346a4697b44291
F ext/fts5/fts5_index.c 9ff3008e903aa9077b0a7a7aa76ab6080eb07a36
F ext/fts5/fts5_storage.c 7848d8f8528d798bba159900ea310a6d4a279da8
F ext/fts5/fts5parse.y 777da8e5819f75c217982c79c29d014c293acac9
@ -597,8 +597,9 @@ F test/fts4noti.test 524807f0c36d49deea7920cdd4cd687408b58849
F test/fts4unicode.test 01ec3fe2a7c3cfff3b4c0581b83caa11b33efa36
F test/fts5aa.test c8d3b9694f6b2864161c7437408464a535d19343
F test/fts5ab.test dc04ed48cf93ca957d174406e6c192f2ff4f3397
F test/fts5ac.test 84599f8253abc7e10b929b8ee0b47c5edd4eafbd
F test/fts5ac.test 9be418d037763f4cc5d86f4239db41fc86bb4f85
F test/fts5ad.test 2ed38bbc865678cb2905247120d02ebba7f20e07
F test/fts5ae.test 5d5ffba68e850d9ade99cdd3f5c6431c82dad81d
F test/fts5ea.test ff43b40f8879ba50b82def70f2ab67c195d1a1d4
F test/full.test 6b3c8fb43c6beab6b95438c1675374b95fab245d
F test/func.test ae97561957aba6ca9e3a7b8a13aac41830d701ef
@ -1194,7 +1195,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 c1f9a4b76c0bbc1ef9f6cdb5d62aa5d536fdf38e
R c3e4d3fb829636894b73dbf001062a3f
P 5808f30fae0d844c52a785bf18872be371d4af68
R c645036fa73431553c03d7990bbe09ec
U dan
Z 4c19ad3988f765a5de2b9174d276eba9
Z 973aba4d2c5c8ae6a3e94f9739fdfe1b

View File

@ -1 +1 @@
5808f30fae0d844c52a785bf18872be371d4af68
16352d3654d5672cd0251db51dbe19f779373feb

View File

@ -309,21 +309,57 @@ foreach {tn expr} {
2.3 "d+c OR u"
2.4 "d+c OR u+d"
3.1 { a AND b AND c }
} {
set res [matchdata 1 $expr]
do_execsql_test 2.1.$tn.[llength $res] {
do_execsql_test 2.$tn.[llength $res] {
SELECT rowid, fts5_test(xx, 'poslist') FROM xx WHERE xx match $expr
} $res
}
#-------------------------------------------------------------------------
# Queries on a specific column.
#
foreach {tn expr} {
1 "x:a"
2 "y:a"
3 "x:b"
4 "y:b"
} {
set res [matchdata 1 $expr]
do_execsql_test 3.$tn.[llength $res] {
SELECT rowid, fts5_test(xx, 'poslist') FROM xx WHERE xx match $expr
} $res
}
do_test 2.1 { poslist {{a b c}} -- a } {0.0}
do_test 2.2 { poslist {{a b c}} -- c } {0.2}
#-------------------------------------------------------------------------
# Some NEAR queries.
#
foreach {tn expr} {
1 "NEAR(a b)"
2 "NEAR(r c)"
2 { NEAR(r c, 5) }
3 { NEAR(r c, 3) }
4 { NEAR(r c, 2) }
5 { NEAR(r c, 0) }
6 { NEAR(a b c) }
7 { NEAR(a b c, 8) }
8 { x : NEAR(r c) }
9 { y : NEAR(r c) }
} {
set res [matchdata 1 $expr]
do_execsql_test 4.1.$tn.[llength $res] {
SELECT rowid, fts5_test(xx, 'poslist') FROM xx WHERE xx match $expr
} $res
}
do_test 4.1 { poslist {{a b c}} -- a } {0.0}
do_test 4.2 { poslist {{a b c}} -- c } {0.2}
foreach {tn expr tclexpr} {
1 {a b} {[N $x -- {a}] && [N $x -- {b}]}
} {
do_execsql_test 3.$tn {SELECT fts5_expr_tcl($expr, 'N $x')} [list $tclexpr]
do_execsql_test 5.$tn {SELECT fts5_expr_tcl($expr, 'N $x')} [list $tclexpr]
}
#-------------------------------------------------------------------------
@ -356,7 +392,7 @@ foreach {bAsc sql} {
19 { c NOT b OR a AND d }
} {
set res [matchdata 0 $expr $bAsc]
do_execsql_test 4.$bAsc.$tn.[llength $res] $sql $res
do_execsql_test 6.$bAsc.$tn.[llength $res] $sql $res
}
}

136
test/fts5ae.test Normal file
View File

@ -0,0 +1,136 @@
# 2014 June 17
#
# 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.
#
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix fts5ae
# If SQLITE_ENABLE_FTS3 is defined, omit this file.
ifcapable !fts3 {
finish_test
return
}
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE t1 USING fts5(a, b);
INSERT INTO t1(t1) VALUES('pgsz=32');
}
do_execsql_test 1.1 {
INSERT INTO t1 VALUES('hello', 'world');
SELECT rowid FROM t1 WHERE t1 MATCH 'hello' ORDER BY rowid ASC;
} {1}
do_execsql_test 1.2 {
INSERT INTO t1 VALUES('world', 'hello');
SELECT rowid FROM t1 WHERE t1 MATCH 'hello' ORDER BY rowid ASC;
} {1 2}
do_execsql_test 1.3 {
INSERT INTO t1 VALUES('world', 'world');
SELECT rowid FROM t1 WHERE t1 MATCH 'hello' ORDER BY rowid ASC;
} {1 2}
do_execsql_test 1.4.1 {
INSERT INTO t1 VALUES('hello', 'hello');
}
do_execsql_test 1.4.2 {
SELECT rowid FROM t1 WHERE t1 MATCH 'hello' ORDER BY rowid ASC;
} {1 2 4}
#-------------------------------------------------------------------------
#
do_execsql_test 2.0 {
CREATE VIRTUAL TABLE t2 USING fts5(x, y);
INSERT INTO t2 VALUES('u t l w w m s', 'm f m o l t k o p e');
INSERT INTO t2 VALUES('f g q e l n d m z x q', 'z s i i i m f w w f n g p');
}
do_execsql_test 2.1 {
SELECT rowid, fts5_test(t2, 'poslist') FROM t2
WHERE t2 MATCH 'm' ORDER BY rowid;
} {
1 {{0.5 1.0 1.2}}
2 {{0.7 1.5}}
}
do_execsql_test 2.2 {
SELECT rowid, fts5_test(t2, 'poslist') FROM t2
WHERE t2 MATCH 'u OR q' ORDER BY rowid;
} {
1 {0.0 {}}
2 {{} {0.2 0.10}}
}
do_execsql_test 2.3 {
SELECT rowid, fts5_test(t2, 'poslist') FROM t2
WHERE t2 MATCH 'y:o' ORDER BY rowid;
} {
1 {{1.3 1.7}}
}
#-------------------------------------------------------------------------
#
do_execsql_test 3.0 {
CREATE VIRTUAL TABLE t3 USING fts5(x, y);
INSERT INTO t3 VALUES( 'j f h o x x a z g b a f a m i b', 'j z c z y x w t');
INSERT INTO t3 VALUES( 'r c', '');
}
do_execsql_test 3.1 {
SELECT rowid, fts5_test(t3, 'poslist') FROM t3 WHERE t3 MATCH 'NEAR(a b)';
} {
1 {{0.6 0.10 0.12} {0.9 0.15}}
}
do_execsql_test 3.2 {
SELECT rowid, fts5_test(t3, 'poslist') FROM t3 WHERE t3 MATCH 'NEAR(r c)';
} {
2 {0.0 0.1}
}
do_execsql_test 3.3 {
INSERT INTO t3
VALUES('k x j r m a d o i z j', 'r t t t f e b r x i v j v g o');
SELECT rowid, fts5_test(t3, 'poslist')
FROM t3 WHERE t3 MATCH 'a OR b AND c';
} {
3 {0.5 {} {}}
1 {{0.6 0.10 0.12} {0.9 0.15} 1.2}
}
#-------------------------------------------------------------------------
#
do_execsql_test 4.0 {
CREATE VIRTUAL TABLE t4 USING fts5(x, y);
INSERT INTO t4
VALUES('k x j r m a d o i z j', 'r t t t f e b r x i v j v g o');
}
breakpoint
do_execsql_test 4.1 {
SELECT rowid, fts5_test(t4, 'poslist') FROM t4 WHERE t4 MATCH 'a OR b AND c';
} {
1 {0.5 {} {}}
}
#93 {0.5 1.6 {}}
finish_test