1
0
mirror of https://github.com/sqlite/sqlite.git synced 2026-01-06 08:01:16 +03:00

Allow fts5 to filter on multiple MATCH clauses in a single scan.

FossilOrigin-Name: 9d418a7a491761eeb38a70898677a493e2631e5d62e75ee88431f52d3dfd2344
This commit is contained in:
dan
2019-09-12 19:38:40 +00:00
parent acf6e08d20
commit 685b2ee0c3
10 changed files with 349 additions and 120 deletions

View File

@@ -695,6 +695,7 @@ int sqlite3Fts5ExprEof(Fts5Expr*);
i64 sqlite3Fts5ExprRowid(Fts5Expr*); i64 sqlite3Fts5ExprRowid(Fts5Expr*);
void sqlite3Fts5ExprFree(Fts5Expr*); void sqlite3Fts5ExprFree(Fts5Expr*);
int sqlite3Fts5ExprAnd(Fts5Expr **pp1, Fts5Expr *p2);
/* Called during startup to register a UDF with SQLite */ /* Called during startup to register a UDF with SQLite */
int sqlite3Fts5ExprInit(Fts5Global*, sqlite3*); int sqlite3Fts5ExprInit(Fts5Global*, sqlite3*);

View File

@@ -683,7 +683,7 @@ int sqlite3Fts5ConfigDeclareVtab(Fts5Config *pConfig){
rc = sqlite3_declare_vtab(pConfig->db, zSql); rc = sqlite3_declare_vtab(pConfig->db, zSql);
sqlite3_free(zSql); sqlite3_free(zSql);
} }
return rc; return rc;
} }

View File

@@ -309,6 +309,42 @@ void sqlite3Fts5ExprFree(Fts5Expr *p){
} }
} }
int sqlite3Fts5ExprAnd(Fts5Expr **pp1, Fts5Expr *p2){
Fts5Parse sParse;
memset(&sParse, 0, sizeof(sParse));
if( *pp1 ){
Fts5Expr *p1 = *pp1;
int nPhrase = p1->nPhrase + p2->nPhrase;
p1->pRoot = sqlite3Fts5ParseNode(&sParse, FTS5_AND, p1->pRoot, p2->pRoot,0);
p2->pRoot = 0;
if( sParse.rc==SQLITE_OK ){
Fts5ExprPhrase **ap = (Fts5ExprPhrase**)sqlite3_realloc(
p1->apExprPhrase, nPhrase * sizeof(Fts5ExprPhrase*)
);
if( ap==0 ){
sParse.rc = SQLITE_NOMEM;
}else{
int i;
memmove(&ap[p2->nPhrase], ap, p1->nPhrase*sizeof(Fts5ExprPhrase*));
for(i=0; i<p2->nPhrase; i++){
ap[i] = p2->apExprPhrase[i];
}
p1->nPhrase = nPhrase;
p1->apExprPhrase = ap;
}
}
sqlite3_free(p2->apExprPhrase);
sqlite3_free(p2);
}else{
*pp1 = p2;
}
return sParse.rc;
}
/* /*
** Argument pTerm must be a synonym iterator. Return the current rowid ** Argument pTerm must be a synonym iterator. Return the current rowid
** that it points to. ** that it points to.

View File

@@ -465,17 +465,39 @@ static void fts5SetUniqueFlag(sqlite3_index_info *pIdxInfo){
** Implementation of the xBestIndex method for FTS5 tables. Within the ** Implementation of the xBestIndex method for FTS5 tables. Within the
** WHERE constraint, it searches for the following: ** WHERE constraint, it searches for the following:
** **
** 1. A MATCH constraint against the special column. ** 1. A MATCH constraint against the table column.
** 2. A MATCH constraint against the "rank" column. ** 2. A MATCH constraint against the "rank" column.
** 3. An == constraint against the rowid column. ** 3. A MATCH constraint against some other column.
** 4. A < or <= constraint against the rowid column. ** 4. An == constraint against the rowid column.
** 5. A > or >= constraint against the rowid column. ** 5. A < or <= constraint against the rowid column.
** 6. A > or >= constraint against the rowid column.
** **
** Within the ORDER BY, either: ** Within the ORDER BY, the following are supported:
** **
** 5. ORDER BY rank [ASC|DESC] ** 5. ORDER BY rank [ASC|DESC]
** 6. ORDER BY rowid [ASC|DESC] ** 6. ORDER BY rowid [ASC|DESC]
** **
** Information for the xFilter call is passed via both the idxNum and
** idxStr variables. Specifically, idxNum is a bitmask of the following
** flags used to encode the ORDER BY clause:
**
** FTS5_BI_ORDER_RANK
** FTS5_BI_ORDER_ROWID
** FTS5_BI_ORDER_DESC
**
** idxStr is used to encode data from the WHERE clause. For each argument
** passed to the xFilter method, the following is appended to idxStr:
**
** Match against table column: "m"
** Match against rank column: "r"
** Match against other column: "<column-number>"
** Equality constraint against the rowid: "="
** A < or <= against the rowid: "<"
** A > or >= against the rowid: ">"
**
** This function ensures that there is at most one "r" or "=". And that if
** there exists an "=" then there is no "<" or ">".
**
** Costs are assigned as follows: ** Costs are assigned as follows:
** **
** a) If an unusable MATCH operator is present in the WHERE clause, the ** a) If an unusable MATCH operator is present in the WHERE clause, the
@@ -503,32 +525,18 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
Fts5Config *pConfig = pTab->pConfig; Fts5Config *pConfig = pTab->pConfig;
const int nCol = pConfig->nCol; const int nCol = pConfig->nCol;
int idxFlags = 0; /* Parameter passed through to xFilter() */ int idxFlags = 0; /* Parameter passed through to xFilter() */
int bHasMatch;
int iNext;
int i; int i;
struct Constraint { char *idxStr;
int op; /* Mask against sqlite3_index_constraint.op */ int iIdxStr = 0;
int fts5op; /* FTS5 mask for idxFlags */ int iCons = 0;
int iCol; /* 0==rowid, 1==tbl, 2==rank */
int omit; /* True to omit this if found */ int bSeenEq = 0;
int iConsIndex; /* Index in pInfo->aConstraint[] */ int bSeenGt = 0;
} aConstraint[] = { int bSeenLt = 0;
{SQLITE_INDEX_CONSTRAINT_MATCH|SQLITE_INDEX_CONSTRAINT_EQ, int bSeenMatch = 0;
FTS5_BI_MATCH, 1, 1, -1}, int bSeenRank = 0;
{SQLITE_INDEX_CONSTRAINT_MATCH|SQLITE_INDEX_CONSTRAINT_EQ,
FTS5_BI_RANK, 2, 1, -1},
{SQLITE_INDEX_CONSTRAINT_EQ, FTS5_BI_ROWID_EQ, 0, 0, -1},
{SQLITE_INDEX_CONSTRAINT_LT|SQLITE_INDEX_CONSTRAINT_LE,
FTS5_BI_ROWID_LE, 0, 0, -1},
{SQLITE_INDEX_CONSTRAINT_GT|SQLITE_INDEX_CONSTRAINT_GE,
FTS5_BI_ROWID_GE, 0, 0, -1},
};
int aColMap[3];
aColMap[0] = -1;
aColMap[1] = nCol;
aColMap[2] = nCol+1;
assert( SQLITE_INDEX_CONSTRAINT_EQ<SQLITE_INDEX_CONSTRAINT_MATCH ); assert( SQLITE_INDEX_CONSTRAINT_EQ<SQLITE_INDEX_CONSTRAINT_MATCH );
assert( SQLITE_INDEX_CONSTRAINT_GT<SQLITE_INDEX_CONSTRAINT_MATCH ); assert( SQLITE_INDEX_CONSTRAINT_GT<SQLITE_INDEX_CONSTRAINT_MATCH );
@@ -543,40 +551,76 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
return SQLITE_ERROR; return SQLITE_ERROR;
} }
/* Set idxFlags flags for all WHERE clause terms that will be used. */ idxStr = (char*)sqlite3_malloc(pInfo->nConstraint * 6 + 1);
if( idxStr==0 ) return SQLITE_NOMEM;
pInfo->idxStr = idxStr;
pInfo->needToFreeIdxStr = 1;
for(i=0; i<pInfo->nConstraint; i++){ for(i=0; i<pInfo->nConstraint; i++){
struct sqlite3_index_constraint *p = &pInfo->aConstraint[i]; struct sqlite3_index_constraint *p = &pInfo->aConstraint[i];
int iCol = p->iColumn; int iCol = p->iColumn;
if( p->op==SQLITE_INDEX_CONSTRAINT_MATCH
if( (p->op==SQLITE_INDEX_CONSTRAINT_MATCH && iCol>=0 && iCol<=nCol) || (p->op==SQLITE_INDEX_CONSTRAINT_EQ && iCol>=nCol)
|| (p->op==SQLITE_INDEX_CONSTRAINT_EQ && iCol==nCol)
){ ){
/* A MATCH operator or equivalent */ /* A MATCH operator or equivalent */
if( p->usable ){ if( p->usable==0 || iCol<0 ){
idxFlags = (idxFlags & 0xFFFF) | FTS5_BI_MATCH | (iCol << 16);
aConstraint[0].iConsIndex = i;
}else{
/* As there exists an unusable MATCH constraint this is an /* As there exists an unusable MATCH constraint this is an
** unusable plan. Set a prohibitively high cost. */ ** unusable plan. Set a prohibitively high cost. */
pInfo->estimatedCost = 1e50; pInfo->estimatedCost = 1e50;
return SQLITE_OK; return SQLITE_OK;
}else{
if( iCol==nCol+1 ){
if( bSeenRank ) continue;
idxStr[iIdxStr++] = 'r';
bSeenRank = 1;
}else{
bSeenMatch = 1;
idxStr[iIdxStr++] = 'm';
if( iCol<nCol ){
sqlite3_snprintf(6, &idxStr[iIdxStr], "%d", iCol);
idxStr += strlen(&idxStr[iIdxStr]);
assert( idxStr[iIdxStr]=='\0' );
}
}
pInfo->aConstraintUsage[i].argvIndex = ++iCons;
pInfo->aConstraintUsage[i].omit = 1;
} }
}else if( p->op<=SQLITE_INDEX_CONSTRAINT_MATCH ){ }
int j; else if( p->usable && bSeenEq==0
for(j=1; j<ArraySize(aConstraint); j++){ && p->op==SQLITE_INDEX_CONSTRAINT_EQ && iCol<0
struct Constraint *pC = &aConstraint[j]; ){
if( iCol==aColMap[pC->iCol] && (p->op & pC->op) && p->usable ){ idxStr[iIdxStr++] = '=';
pC->iConsIndex = i; bSeenEq = 1;
idxFlags |= pC->fts5op; pInfo->aConstraintUsage[i].argvIndex = ++iCons;
}
}
if( bSeenEq==0 ){
for(i=0; i<pInfo->nConstraint; i++){
struct sqlite3_index_constraint *p = &pInfo->aConstraint[i];
if( p->iColumn<0 && p->usable ){
int op = p->op;
if( op==SQLITE_INDEX_CONSTRAINT_LT || op==SQLITE_INDEX_CONSTRAINT_LE ){
if( bSeenLt ) continue;
idxStr[iIdxStr++] = '<';
pInfo->aConstraintUsage[i].argvIndex = ++iCons;
bSeenLt = 1;
}else
if( op==SQLITE_INDEX_CONSTRAINT_GT || op==SQLITE_INDEX_CONSTRAINT_GE ){
if( bSeenGt ) continue;
idxStr[iIdxStr++] = '>';
pInfo->aConstraintUsage[i].argvIndex = ++iCons;
bSeenGt = 1;
} }
} }
} }
} }
idxStr[iIdxStr] = '\0';
/* Set idxFlags flags for the ORDER BY clause */ /* Set idxFlags flags for the ORDER BY clause */
if( pInfo->nOrderBy==1 ){ if( pInfo->nOrderBy==1 ){
int iSort = pInfo->aOrderBy[0].iColumn; int iSort = pInfo->aOrderBy[0].iColumn;
if( iSort==(pConfig->nCol+1) && BitFlagTest(idxFlags, FTS5_BI_MATCH) ){ if( iSort==(pConfig->nCol+1) && bSeenMatch ){
idxFlags |= FTS5_BI_ORDER_RANK; idxFlags |= FTS5_BI_ORDER_RANK;
}else if( iSort==-1 ){ }else if( iSort==-1 ){
idxFlags |= FTS5_BI_ORDER_ROWID; idxFlags |= FTS5_BI_ORDER_ROWID;
@@ -590,26 +634,15 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
} }
/* Calculate the estimated cost based on the flags set in idxFlags. */ /* Calculate the estimated cost based on the flags set in idxFlags. */
bHasMatch = BitFlagTest(idxFlags, FTS5_BI_MATCH); if( bSeenEq ){
if( BitFlagTest(idxFlags, FTS5_BI_ROWID_EQ) ){ pInfo->estimatedCost = bSeenMatch ? 100.0 : 10.0;
pInfo->estimatedCost = bHasMatch ? 100.0 : 10.0; if( bSeenMatch==0 ) fts5SetUniqueFlag(pInfo);
if( bHasMatch==0 ) fts5SetUniqueFlag(pInfo); }else if( bSeenLt && bSeenGt ){
}else if( BitFlagAllTest(idxFlags, FTS5_BI_ROWID_LE|FTS5_BI_ROWID_GE) ){ pInfo->estimatedCost = bSeenMatch ? 500.0 : 250000.0;
pInfo->estimatedCost = bHasMatch ? 500.0 : 250000.0; }else if( bSeenLt || bSeenGt ){
}else if( BitFlagTest(idxFlags, FTS5_BI_ROWID_LE|FTS5_BI_ROWID_GE) ){ pInfo->estimatedCost = bSeenMatch ? 750.0 : 750000.0;
pInfo->estimatedCost = bHasMatch ? 750.0 : 750000.0;
}else{ }else{
pInfo->estimatedCost = bHasMatch ? 1000.0 : 1000000.0; pInfo->estimatedCost = bSeenMatch ? 1000.0 : 1000000.0;
}
/* Assign argvIndex values to each constraint in use. */
iNext = 1;
for(i=0; i<ArraySize(aConstraint); i++){
struct Constraint *pC = &aConstraint[i];
if( pC->iConsIndex>=0 ){
pInfo->aConstraintUsage[pC->iConsIndex].argvIndex = iNext++;
pInfo->aConstraintUsage[pC->iConsIndex].omit = (unsigned char)pC->omit;
}
} }
pInfo->idxNum = idxFlags; pInfo->idxNum = idxFlags;
@@ -1132,7 +1165,7 @@ static i64 fts5GetRowidLimit(sqlite3_value *pVal, i64 iDefault){
static int fts5FilterMethod( static int fts5FilterMethod(
sqlite3_vtab_cursor *pCursor, /* The cursor used for this query */ sqlite3_vtab_cursor *pCursor, /* The cursor used for this query */
int idxNum, /* Strategy index */ int idxNum, /* Strategy index */
const char *zUnused, /* Unused */ const char *idxStr, /* Unused */
int nVal, /* Number of elements in apVal */ int nVal, /* Number of elements in apVal */
sqlite3_value **apVal /* Arguments for the indexing scheme */ sqlite3_value **apVal /* Arguments for the indexing scheme */
){ ){
@@ -1140,7 +1173,6 @@ static int fts5FilterMethod(
Fts5Config *pConfig = pTab->p.pConfig; Fts5Config *pConfig = pTab->p.pConfig;
Fts5Cursor *pCsr = (Fts5Cursor*)pCursor; Fts5Cursor *pCsr = (Fts5Cursor*)pCursor;
int rc = SQLITE_OK; /* Error code */ int rc = SQLITE_OK; /* Error code */
int iVal = 0; /* Counter for apVal[] */
int bDesc; /* True if ORDER BY [rank|rowid] DESC */ int bDesc; /* True if ORDER BY [rank|rowid] DESC */
int bOrderByRank; /* True if ORDER BY rank */ int bOrderByRank; /* True if ORDER BY rank */
sqlite3_value *pMatch = 0; /* <tbl> MATCH ? expression (or NULL) */ sqlite3_value *pMatch = 0; /* <tbl> MATCH ? expression (or NULL) */
@@ -1150,9 +1182,9 @@ static int fts5FilterMethod(
sqlite3_value *pRowidGe = 0; /* rowid >= ? expression (or NULL) */ sqlite3_value *pRowidGe = 0; /* rowid >= ? expression (or NULL) */
int iCol; /* Column on LHS of MATCH operator */ int iCol; /* Column on LHS of MATCH operator */
char **pzErrmsg = pConfig->pzErrmsg; char **pzErrmsg = pConfig->pzErrmsg;
int i;
UNUSED_PARAM(zUnused); int iIdxStr = 0;
UNUSED_PARAM(nVal); Fts5Expr *pExpr = 0;
if( pCsr->ePlan ){ if( pCsr->ePlan ){
fts5FreeCursorComponents(pCsr); fts5FreeCursorComponents(pCsr);
@@ -1165,23 +1197,60 @@ static int fts5FilterMethod(
assert( pCsr->pRank==0 ); assert( pCsr->pRank==0 );
assert( pCsr->zRank==0 ); assert( pCsr->zRank==0 );
assert( pCsr->zRankArgs==0 ); assert( pCsr->zRankArgs==0 );
assert( pTab->pSortCsr==0 || nVal==0 );
assert( pzErrmsg==0 || pzErrmsg==&pTab->p.base.zErrMsg ); assert( pzErrmsg==0 || pzErrmsg==&pTab->p.base.zErrMsg );
pConfig->pzErrmsg = &pTab->p.base.zErrMsg; pConfig->pzErrmsg = &pTab->p.base.zErrMsg;
/* Decode the arguments passed through to this function. /* Decode the arguments passed through to this function. */
** for(i=0; i<nVal; i++){
** Note: The following set of if(...) statements must be in the same switch( idxStr[iIdxStr++] ){
** order as the corresponding entries in the struct at the top of case 'r':
** fts5BestIndexMethod(). */ pRank = apVal[i];
if( BitFlagTest(idxNum, FTS5_BI_MATCH) ) pMatch = apVal[iVal++]; break;
if( BitFlagTest(idxNum, FTS5_BI_RANK) ) pRank = apVal[iVal++]; case 'm': {
if( BitFlagTest(idxNum, FTS5_BI_ROWID_EQ) ) pRowidEq = apVal[iVal++]; char *zText = sqlite3_value_text(apVal[i]);
if( BitFlagTest(idxNum, FTS5_BI_ROWID_LE) ) pRowidLe = apVal[iVal++]; if( zText==0 ) zText = "";
if( BitFlagTest(idxNum, FTS5_BI_ROWID_GE) ) pRowidGe = apVal[iVal++];
iCol = (idxNum>>16); if( idxStr[iIdxStr]>='0' && idxStr[iIdxStr]<='9' ){
assert( iCol>=0 && iCol<=pConfig->nCol ); iCol = 0;
assert( iVal==nVal ); do{
iCol = iCol*10 + (idxStr[iIdxStr]-'0');
iIdxStr++;
}while( idxStr[iIdxStr]>='0' && idxStr[iIdxStr]<='9' );
}else{
iCol = pConfig->nCol;
}
if( zText[0]=='*' ){
/* The user has issued a query of the form "MATCH '*...'". This
** indicates that the MATCH expression is not a full text query,
** but a request for an internal parameter. */
rc = fts5SpecialMatch(pTab, pCsr, &zText[1]);
goto filter_out;
}else{
char **pzErr = &pTab->p.base.zErrMsg;
rc = sqlite3Fts5ExprNew(pConfig, iCol, zText, &pExpr, pzErr);
if( rc==SQLITE_OK ){
rc = sqlite3Fts5ExprAnd(&pCsr->pExpr, pExpr);
pExpr = 0;
}
if( rc!=SQLITE_OK ) goto filter_out;
}
break;
}
case '=':
pRowidEq = apVal[i];
break;
case '<':
pRowidLe = apVal[i];
break;
default: assert( idxStr[iIdxStr-1]=='>' );
pRowidGe = apVal[i];
break;
}
}
bOrderByRank = ((idxNum & FTS5_BI_ORDER_RANK) ? 1 : 0); bOrderByRank = ((idxNum & FTS5_BI_ORDER_RANK) ? 1 : 0);
pCsr->bDesc = bDesc = ((idxNum & FTS5_BI_ORDER_DESC) ? 1 : 0); pCsr->bDesc = bDesc = ((idxNum & FTS5_BI_ORDER_DESC) ? 1 : 0);
@@ -1221,29 +1290,15 @@ static int fts5FilterMethod(
pCsr->ePlan = FTS5_PLAN_SOURCE; pCsr->ePlan = FTS5_PLAN_SOURCE;
pCsr->pExpr = pTab->pSortCsr->pExpr; pCsr->pExpr = pTab->pSortCsr->pExpr;
rc = fts5CursorFirst(pTab, pCsr, bDesc); rc = fts5CursorFirst(pTab, pCsr, bDesc);
}else if( pMatch ){ }else if( pCsr->pExpr ){
const char *zExpr = (const char*)sqlite3_value_text(apVal[0]);
if( zExpr==0 ) zExpr = "";
rc = fts5CursorParseRank(pConfig, pCsr, pRank); rc = fts5CursorParseRank(pConfig, pCsr, pRank);
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
if( zExpr[0]=='*' ){ if( bOrderByRank ){
/* The user has issued a query of the form "MATCH '*...'". This pCsr->ePlan = FTS5_PLAN_SORTED_MATCH;
** indicates that the MATCH expression is not a full text query, rc = fts5CursorFirstSorted(pTab, pCsr, bDesc);
** but a request for an internal parameter. */
rc = fts5SpecialMatch(pTab, pCsr, &zExpr[1]);
}else{ }else{
char **pzErr = &pTab->p.base.zErrMsg; pCsr->ePlan = FTS5_PLAN_MATCH;
rc = sqlite3Fts5ExprNew(pConfig, iCol, zExpr, &pCsr->pExpr, pzErr); rc = fts5CursorFirst(pTab, pCsr, bDesc);
if( rc==SQLITE_OK ){
if( bOrderByRank ){
pCsr->ePlan = FTS5_PLAN_SORTED_MATCH;
rc = fts5CursorFirstSorted(pTab, pCsr, bDesc);
}else{
pCsr->ePlan = FTS5_PLAN_MATCH;
rc = fts5CursorFirst(pTab, pCsr, bDesc);
}
}
} }
} }
}else if( pConfig->zContent==0 ){ }else if( pConfig->zContent==0 ){
@@ -1260,7 +1315,7 @@ static int fts5FilterMethod(
); );
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
if( pCsr->ePlan==FTS5_PLAN_ROWID ){ if( pCsr->ePlan==FTS5_PLAN_ROWID ){
sqlite3_bind_value(pCsr->pStmt, 1, apVal[0]); sqlite3_bind_value(pCsr->pStmt, 1, pRowidEq);
}else{ }else{
sqlite3_bind_int64(pCsr->pStmt, 1, pCsr->iFirstRowid); sqlite3_bind_int64(pCsr->pStmt, 1, pCsr->iFirstRowid);
sqlite3_bind_int64(pCsr->pStmt, 2, pCsr->iLastRowid); sqlite3_bind_int64(pCsr->pStmt, 2, pCsr->iLastRowid);
@@ -1269,6 +1324,8 @@ static int fts5FilterMethod(
} }
} }
filter_out:
sqlite3Fts5ExprFree(pExpr);
pConfig->pzErrmsg = pzErrmsg; pConfig->pzErrmsg = pzErrmsg;
return rc; return rc;
} }

View File

@@ -147,5 +147,27 @@ do_faultsim_test 5.1 -faults oom* -body {
faultsim_test_result {0 {1 4}} faultsim_test_result {0 {1 4}}
} }
#-------------------------------------------------------------------------
# Test OOM injection in a query with two MATCH expressions
#
reset_db
do_execsql_test 6.0 {
CREATE VIRTUAL TABLE t1 USING fts5(a);
INSERT INTO t1 VALUES('a b c d'); -- 1
INSERT INTO t1 VALUES('d a b c'); -- 2
INSERT INTO t1 VALUES('c d a b'); -- 3
INSERT INTO t1 VALUES('b c d a'); -- 4
}
do_faultsim_test 6.1 -faults oom* -body {
execsql { SELECT rowid FROM t1 WHERE t1 MATCH 'a' AND t1 MATCH 'b' }
} -test {
faultsim_test_result {0 {1 2 3 4}}
}
do_faultsim_test 6.2 -faults oom* -body {
execsql { SELECT rowid FROM t1 WHERE t1 MATCH 'a OR b' AND t1 MATCH 'c OR d' }
} -test {
faultsim_test_result {0 {1 2 3 4}}
}
finish_test finish_test

View File

@@ -0,0 +1,99 @@
# 2014 September 13
#
# 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.
#
source [file join [file dirname [info script]] fts5_common.tcl]
set testprefix fts5multi
# If SQLITE_ENABLE_FTS5 is not defined, omit this file.
ifcapable !fts5 {
finish_test
return
}
fts5_aux_test_functions db
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE t1 USING fts5(a, b, c);
INSERT INTO t1 VALUES('gg bb bb' ,'gg ff gg' ,'ii ii');
INSERT INTO t1 VALUES('dd dd hh kk','jj' ,'aa');
INSERT INTO t1 VALUES('kk gg ee' ,'hh cc' ,'hh jj aa cc');
INSERT INTO t1 VALUES('hh' ,'bb jj cc' ,'kk ii');
INSERT INTO t1 VALUES('kk dd kk ii','aa ee aa' ,'ee');
INSERT INTO t1 VALUES('ee' ,'ff gg kk aa','ee ff ee');
INSERT INTO t1 VALUES('ff jj' ,'gg ee' ,'kk ee gg kk');
INSERT INTO t1 VALUES('ff ee dd hh','kk ee' ,'gg dd');
INSERT INTO t1 VALUES('bb' ,'aa' ,'bb aa');
INSERT INTO t1 VALUES('hh cc bb' ,'ff bb' ,'cc');
INSERT INTO t1 VALUES('jj' ,'ff dd bb aa','dd dd ff ff');
INSERT INTO t1 VALUES('ff dd gg dd','gg aa bb ff','cc');
INSERT INTO t1 VALUES('ff aa cc jj','kk' ,'ii dd');
INSERT INTO t1 VALUES('jj dd' ,'cc' ,'ii hh ee aa');
INSERT INTO t1 VALUES('ff ii hh' ,'dd' ,'gg');
INSERT INTO t1 VALUES('ff dd gg hh','hh' ,'ff dd');
INSERT INTO t1 VALUES('cc cc' ,'ff dd ff' ,'bb');
INSERT INTO t1 VALUES('ii' ,'bb ii' ,'jj kk');
INSERT INTO t1 VALUES('ff hh' ,'hh bb' ,'bb dd ee');
INSERT INTO t1 VALUES('jj kk' ,'jj' ,'gg ff cc');
INSERT INTO t1 VALUES('dd kk' ,'ii gg' ,'dd');
INSERT INTO t1 VALUES('cc' ,'aa ff' ,'ii');
INSERT INTO t1 VALUES('bb ff bb ii','bb kk bb aa','hh ff ii dd');
INSERT INTO t1 VALUES('aa' ,'ee bb jj jj','dd');
INSERT INTO t1 VALUES('kk dd cc' ,'aa jj' ,'ee aa ff');
INSERT INTO t1 VALUES('aa gg aa' ,'jj' ,'ii kk hh gg');
INSERT INTO t1 VALUES('ff hh aa' ,'jj ii' ,'hh dd bb jj');
INSERT INTO t1 VALUES('hh' ,'aa gg kk' ,'bb ee');
INSERT INTO t1 VALUES('bb' ,'ee' ,'gg');
INSERT INTO t1 VALUES('dd kk' ,'kk bb aa' ,'ee');
}
foreach {tn c1 e1 c2 e2} {
1 t1 aa t1 bb
2 a aa b bb
3 a "aa OR bb OR cc" b "jj OR ii OR hh"
4 t1 "aa AND bb" t1 "cc"
5 c "kk" b "aa OR bb OR cc OR dd OR ee"
} {
if {$c1=="t1"} {
set lhs "( $e1 )"
} else {
set lhs "$c1 : ( $e1 )"
}
if {$c2=="t1"} {
set rhs "( $e2 )"
} else {
set rhs "$c2 : ( $e2 )"
}
set q1 "t1 MATCH '($lhs) AND ($rhs)'"
set q2 "$c1 MATCH '$e1' AND $c2 MATCH '$e2'"
set ret [execsql "SELECT rowid FROM t1 WHERE $q1"]
set N [llength $ret]
do_execsql_test 1.$tn.1.($N) "SELECT rowid FROM t1 WHERE $q2" $ret
set ret [execsql "SELECT fts5_test_poslist(t1) FROM t1 WHERE $q1"]
do_execsql_test 1.$tn.2.($N) "
SELECT fts5_test_poslist(t1) FROM t1 WHERE $q2
" $ret
}
do_catchsql_test 2.1.1 {
SELECT rowid FROM t1 WHERE t1 MATCH '(NOT' AND t1 MATCH 'aa bb';
} {1 {fts5: syntax error near "NOT"}}
do_catchsql_test 2.1.2 {
SELECT rowid FROM t1 WHERE t1 MATCH 'aa bb' AND t1 MATCH '(NOT';
} {1 {fts5: syntax error near "NOT"}}
finish_test

View File

@@ -31,7 +31,7 @@ do_eqp_test 1.1 {
} { } {
QUERY PLAN QUERY PLAN
|--SCAN TABLE t1 |--SCAN TABLE t1
`--SCAN TABLE f1 VIRTUAL TABLE INDEX 65537: `--SCAN TABLE f1 VIRTUAL TABLE INDEX 0:m
} }
do_eqp_test 1.2 { do_eqp_test 1.2 {
@@ -46,7 +46,7 @@ do_eqp_test 1.3 {
SELECT * FROM f1 WHERE f1 MATCH ? ORDER BY ff SELECT * FROM f1 WHERE f1 MATCH ? ORDER BY ff
} { } {
QUERY PLAN QUERY PLAN
|--SCAN TABLE f1 VIRTUAL TABLE INDEX 65537: |--SCAN TABLE f1 VIRTUAL TABLE INDEX 0:m
`--USE TEMP B-TREE FOR ORDER BY `--USE TEMP B-TREE FOR ORDER BY
} }
@@ -60,6 +60,6 @@ do_eqp_test 1.4 {
do_eqp_test 1.5 { do_eqp_test 1.5 {
SELECT * FROM f1 WHERE rank MATCH ? SELECT * FROM f1 WHERE rank MATCH ?
} {SCAN TABLE f1 VIRTUAL TABLE INDEX 2:} } {SCAN TABLE f1 VIRTUAL TABLE INDEX 0:r}
finish_test finish_test

View File

@@ -467,4 +467,17 @@ do_execsql_test 21.3 {
SELECT rowid FROM x1($doc); SELECT rowid FROM x1($doc);
} {11112} } {11112}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 22.0 {
CREATE VIRTUAL TABLE x1 USING fts5(x);
INSERT INTO x1(x) VALUES('a b c');
INSERT INTO x1(x) VALUES('x y z');
INSERT INTO x1(x) VALUES('c b a');
INSERT INTO x1(x) VALUES('z y x');
}
do_catchsql_test 22.1 {SELECT * FROM x1('')} {1 {fts5: syntax error near ""}}
do_catchsql_test 22.2 {SELECT * FROM x1(NULL)} {1 {fts5: syntax error near ""}}
finish_test finish_test

View File

@@ -1,5 +1,5 @@
C Fix\stypo\sfor\sone\sinstance\sof\sline\snumber\shandling\sin\sthe\sLemon\stool. C Allow\sfts5\sto\sfilter\son\smultiple\sMATCH\sclauses\sin\sa\ssingle\sscan.
D 2019-09-11T15:25:26.595 D 2019-09-12T19:38:40.141
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -110,14 +110,14 @@ F ext/fts3/unicode/mkunicode.tcl bf7fcaa6d68e6d38223467983785d054f1cff4d9e3905dd
F ext/fts3/unicode/parseunicode.tcl a981bd6466d12dd17967515801c3ff23f74a281be1a03cf1e6f52a6959fc77eb F ext/fts3/unicode/parseunicode.tcl a981bd6466d12dd17967515801c3ff23f74a281be1a03cf1e6f52a6959fc77eb
F ext/fts5/extract_api_docs.tcl a36e54ec777172ddd3f9a88daf593b00848368e0 F ext/fts5/extract_api_docs.tcl a36e54ec777172ddd3f9a88daf593b00848368e0
F ext/fts5/fts5.h 7c9da96f2b9dcfa4dd94081fb2d87ec418d8cdb35b25df56756c334b6b558fd7 F ext/fts5/fts5.h 7c9da96f2b9dcfa4dd94081fb2d87ec418d8cdb35b25df56756c334b6b558fd7
F ext/fts5/fts5Int.h d87fb17d12296613cdec2d1f4213ecd8840d3deb34837b6d3889b830d06baac4 F ext/fts5/fts5Int.h 0ec19a906a54c0e53f8a380c0ff70f11a866aa259490bc13aa39f8d2491800fd
F ext/fts5/fts5_aux.c dcc627d8b6e3fc773db528ff67b39955dab7b51628f9dba8e15849e5bedfd7fa F ext/fts5/fts5_aux.c dcc627d8b6e3fc773db528ff67b39955dab7b51628f9dba8e15849e5bedfd7fa
F ext/fts5/fts5_buffer.c 5a5fe0159752c0fb0a5a93c722e9db2662822709490769d482b76a6dc8aaca70 F ext/fts5/fts5_buffer.c 5a5fe0159752c0fb0a5a93c722e9db2662822709490769d482b76a6dc8aaca70
F ext/fts5/fts5_config.c d7523cba5e66da077233c023aecbc3e6a37978ff75a18131c5ab5b1229d5bac7 F ext/fts5/fts5_config.c 606a29f2962a8f4508923e6ad833974b32a3ab4093f63fd6de0fb33a87eed54c
F ext/fts5/fts5_expr.c 840c88d55e78083a5e61a35968df877712ae28791b347eced1e98e3b337d2d3c F ext/fts5/fts5_expr.c 5661fe64f4f5a499710df9561075de84b743f01e808af46df4130a9ec343a0fd
F ext/fts5/fts5_hash.c 1cc0095646f5f3b46721aa112fb4f9bf29ae175cb5338f89dcec66ed97acfe75 F ext/fts5/fts5_hash.c 1cc0095646f5f3b46721aa112fb4f9bf29ae175cb5338f89dcec66ed97acfe75
F ext/fts5/fts5_index.c b062bdb836e195656aac8d6684e943585cff4bf7d7c593c80cb67c3b6cfef7ee F ext/fts5/fts5_index.c b062bdb836e195656aac8d6684e943585cff4bf7d7c593c80cb67c3b6cfef7ee
F ext/fts5/fts5_main.c 15dc14ea594ff2ea183f5e79c8f6fea14640ac7c4bd5d93ad1d506a4db80c998 F ext/fts5/fts5_main.c e6db945454a0dae2dafcf29905d7d5272b64b00da34a43bd4ce732e2079a159d
F ext/fts5/fts5_storage.c 801b4e3cd33786a60a07b6b01f86d0fbdf7e68325054e08d17176293a8081e99 F ext/fts5/fts5_storage.c 801b4e3cd33786a60a07b6b01f86d0fbdf7e68325054e08d17176293a8081e99
F ext/fts5/fts5_tcl.c 39bcbae507f594aad778172fa914cad0f585bf92fd3b078c686e249282db0d95 F ext/fts5/fts5_tcl.c 39bcbae507f594aad778172fa914cad0f585bf92fd3b078c686e249282db0d95
F ext/fts5/fts5_test_mi.c 08c11ec968148d4cb4119d96d819f8c1f329812c568bac3684f5464be177d3ee F ext/fts5/fts5_test_mi.c 08c11ec968148d4cb4119d96d819f8c1f329812c568bac3684f5464be177d3ee
@@ -176,7 +176,7 @@ F ext/fts5/test/fts5fault7.test 0acbec416edb24b8881f154e99c31e9ccf73f539cfcd1640
F ext/fts5/test/fts5fault8.test 318238659d35f82ad215ecb57ca4c87486ea85d45dbeedaee42f148ff5105ee2 F ext/fts5/test/fts5fault8.test 318238659d35f82ad215ecb57ca4c87486ea85d45dbeedaee42f148ff5105ee2
F ext/fts5/test/fts5fault9.test 098e6b894bbdf9b2192f994a30f4043673fb3f338b6b8ab1624c704422f39119 F ext/fts5/test/fts5fault9.test 098e6b894bbdf9b2192f994a30f4043673fb3f338b6b8ab1624c704422f39119
F ext/fts5/test/fts5faultA.test be4487576bff8c22cee6597d1893b312f306504a8c6ccd3c53ca85af12290c8c F ext/fts5/test/fts5faultA.test be4487576bff8c22cee6597d1893b312f306504a8c6ccd3c53ca85af12290c8c
F ext/fts5/test/fts5faultB.test e6d04f9ea7b21be1d89abb8df2cb4baf65b0453b744d5a805fcd3ef45ff86a7e F ext/fts5/test/fts5faultB.test d606bdb8e81aaeb6f41de3fc9fc7ae315733f0903fbff05cf54f5b045b729ab5
F ext/fts5/test/fts5faultD.test cc5d1225556e356615e719c612e845d41bff7d5a F ext/fts5/test/fts5faultD.test cc5d1225556e356615e719c612e845d41bff7d5a
F ext/fts5/test/fts5first.test 3fcf2365c00a15fc9704233674789a3b95131d12de18a9b996159f6909dc8079 F ext/fts5/test/fts5first.test 3fcf2365c00a15fc9704233674789a3b95131d12de18a9b996159f6909dc8079
F ext/fts5/test/fts5full.test 49b565da02918c06e58f51f0b953b0302b96f155aa68baba24782b81570685e2 F ext/fts5/test/fts5full.test 49b565da02918c06e58f51f0b953b0302b96f155aa68baba24782b81570685e2
@@ -190,12 +190,13 @@ F ext/fts5/test/fts5matchinfo.test 79129ff6c9a2d86943b287a5a8caa7ee639f6dcf004d8
F ext/fts5/test/fts5merge.test e92a8db28b45931e7a9c7b1bbd36101692759d00274df74d83fd29d25d53b3a6 F ext/fts5/test/fts5merge.test e92a8db28b45931e7a9c7b1bbd36101692759d00274df74d83fd29d25d53b3a6
F ext/fts5/test/fts5merge2.test 3ebad1a59d6ad3fb66eff6523a09e95dc6367cbefb3cd73196801dea0425c8e2 F ext/fts5/test/fts5merge2.test 3ebad1a59d6ad3fb66eff6523a09e95dc6367cbefb3cd73196801dea0425c8e2
F ext/fts5/test/fts5misc.test 5becd134b66f7370042968a2b127b92ea7748e249f16cb6a996f450812e89eec F ext/fts5/test/fts5misc.test 5becd134b66f7370042968a2b127b92ea7748e249f16cb6a996f450812e89eec
F ext/fts5/test/fts5multi.test a15bc91cdb717492e6e1b66fec1c356cb57386b980c7ba5af1915f97fe878581
F ext/fts5/test/fts5multiclient.test 5ff811c028d6108045ffef737f1e9f05028af2458e456c0937c1d1b8dea56d45 F ext/fts5/test/fts5multiclient.test 5ff811c028d6108045ffef737f1e9f05028af2458e456c0937c1d1b8dea56d45
F ext/fts5/test/fts5near.test 211477940142d733ac04fad97cb24095513ab2507073a99c2765c3ddd2ef58bd F ext/fts5/test/fts5near.test 211477940142d733ac04fad97cb24095513ab2507073a99c2765c3ddd2ef58bd
F ext/fts5/test/fts5onepass.test f9b7d9b2c334900c6542a869760290e2ab5382af8fbd618834bf1fcc3e7b84da F ext/fts5/test/fts5onepass.test f9b7d9b2c334900c6542a869760290e2ab5382af8fbd618834bf1fcc3e7b84da
F ext/fts5/test/fts5optimize.test 36a752d24c818792032e4ff502936fc9cc5ef938721696396fdc79214b2717f1 F ext/fts5/test/fts5optimize.test 36a752d24c818792032e4ff502936fc9cc5ef938721696396fdc79214b2717f1
F ext/fts5/test/fts5phrase.test 13e5d8e9083077b3d9c74315b3c92ec723cc6eb37c8155e0bfe1bba00559f07b F ext/fts5/test/fts5phrase.test 13e5d8e9083077b3d9c74315b3c92ec723cc6eb37c8155e0bfe1bba00559f07b
F ext/fts5/test/fts5plan.test 00dc4c974938b509db7cb3680407f068ee6e9cc824f492f68cb741a7b679fe41 F ext/fts5/test/fts5plan.test 771b999d161e24fd803ce0290adb7c6e7c9b9cc2c6a0adb344813fb89473aa32
F ext/fts5/test/fts5porter.test 8d08010c28527db66bc3feebd2b8767504aaeb9b101a986342fa7833d49d0d15 F ext/fts5/test/fts5porter.test 8d08010c28527db66bc3feebd2b8767504aaeb9b101a986342fa7833d49d0d15
F ext/fts5/test/fts5porter2.test 0d251a673f02fa13ca7f011654873b3add20745f7402f108600a23e52d8c7457 F ext/fts5/test/fts5porter2.test 0d251a673f02fa13ca7f011654873b3add20745f7402f108600a23e52d8c7457
F ext/fts5/test/fts5prefix.test a0fa67b06650f2deaa7bf27745899d94e0fb547ad9ecbd08bfad98c04912c056 F ext/fts5/test/fts5prefix.test a0fa67b06650f2deaa7bf27745899d94e0fb547ad9ecbd08bfad98c04912c056
@@ -204,7 +205,7 @@ F ext/fts5/test/fts5rank.test c9fd4a1e36b4fa92d572ec13d846469b97da249d1c2f7fd3ee
F ext/fts5/test/fts5rebuild.test 55d6f17715cddbf825680dd6551efbc72ed916d8cf1cde40a46fc5d785b451e7 F ext/fts5/test/fts5rebuild.test 55d6f17715cddbf825680dd6551efbc72ed916d8cf1cde40a46fc5d785b451e7
F ext/fts5/test/fts5restart.test 835ecc8f449e3919f72509ab58056d0cedca40d1fe04108ccf8ac4c2ba41f415 F ext/fts5/test/fts5restart.test 835ecc8f449e3919f72509ab58056d0cedca40d1fe04108ccf8ac4c2ba41f415
F ext/fts5/test/fts5rowid.test b8790ec170a8dc1942a15aef3db926a5f3061b1ff171013003d8297203a20ad6 F ext/fts5/test/fts5rowid.test b8790ec170a8dc1942a15aef3db926a5f3061b1ff171013003d8297203a20ad6
F ext/fts5/test/fts5simple.test 313ad28ef38ebe25f0a1673dd18f2fac446e25feb15bbb0c223a65ea00594f72 F ext/fts5/test/fts5simple.test a298670508c1458b88ce6030440f26a30673931884eb5f4094ac1773b3ba217b
F ext/fts5/test/fts5simple2.test 258a1b0c590409bfa5271e872c79572b319d2a56554d0585f68f146a0da603f0 F ext/fts5/test/fts5simple2.test 258a1b0c590409bfa5271e872c79572b319d2a56554d0585f68f146a0da603f0
F ext/fts5/test/fts5simple3.test d5c74a9d3ca71bd5dd5cacb7c55b86ea12cdddfc8b1910e3de2995206898380f F ext/fts5/test/fts5simple3.test d5c74a9d3ca71bd5dd5cacb7c55b86ea12cdddfc8b1910e3de2995206898380f
F ext/fts5/test/fts5synonym.test 1651815b8008de170e8e600dcacc17521d765482ea8f074ae82cfa870d8bb7fb F ext/fts5/test/fts5synonym.test 1651815b8008de170e8e600dcacc17521d765482ea8f074ae82cfa870d8bb7fb
@@ -1841,7 +1842,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P a97804620a27acc30bebd2aaa04e38f2f36de48b0931038ca8bdc9cb0c36b8f4 P 980be1730dc1239c63a107923bf2e32b4ec7d4bc31b9190e711cc35f18cc2bb4
R 5065e0b2c7f4ffc5b218b4bab44bb95f R f455101857a1853760c9098b8cd2af2d
U mistachkin U dan
Z 05d39d001f8aeefdace9f8a8f5ba54b9 Z 191c94e283a8422844a9aa5307aa3f27

View File

@@ -1 +1 @@
980be1730dc1239c63a107923bf2e32b4ec7d4bc31b9190e711cc35f18cc2bb4 9d418a7a491761eeb38a70898677a493e2631e5d62e75ee88431f52d3dfd2344