Index: ext/fts3/fts3.c ================================================================== --- ext/fts3/fts3.c +++ ext/fts3/fts3.c @@ -1469,10 +1469,23 @@ sqlite3_vtab **ppVtab, /* OUT: New sqlite3_vtab object */ char **pzErr /* OUT: sqlite3_malloc'd error message */ ){ return fts3InitVtab(1, db, pAux, argc, argv, ppVtab, pzErr); } + +/* +** Set the pIdxInfo->estimatedRows variable to nRow. Unless this +** extension is currently being used by a version of SQLite too old to +** support estimatedRows. In that case this function is a no-op. +*/ +static void setEstimatedRows(sqlite3_index_info *pIdxInfo, i64 nRow){ +#if SQLITE_VERSION_NUMBER>=3008002 + if( sqlite3_libversion_number()>=3008002 ){ + pIdxInfo->estimatedRows = nRow; + } +#endif +} /* ** Implementation of the xBestIndex method for FTS3 tables. There ** are three possible strategies, in order of preference: ** @@ -1497,11 +1510,24 @@ pInfo->idxNum = FTS3_FULLSCAN_SEARCH; pInfo->estimatedCost = 5000000; for(i=0; inConstraint; i++){ int bDocid; /* True if this constraint is on docid */ struct sqlite3_index_constraint *pCons = &pInfo->aConstraint[i]; - if( pCons->usable==0 ) continue; + if( pCons->usable==0 ){ + if( pCons->op==SQLITE_INDEX_CONSTRAINT_MATCH ){ + /* There exists an unusable MATCH constraint. This means that if + ** the planner does elect to use the results of this call as part + ** of the overall query plan the user will see an "unable to use + ** function MATCH in the requested context" error. To discourage + ** this, return a very high cost here. */ + pInfo->idxNum = FTS3_FULLSCAN_SEARCH; + pInfo->estimatedCost = 1e50; + setEstimatedRows(pInfo, ((sqlite3_int64)1) << 50); + return SQLITE_OK; + } + continue; + } bDocid = (pCons->iColumn<0 || pCons->iColumn==p->nColumn+1); /* A direct lookup on the rowid or docid column. Assign a cost of 1.0. */ if( iCons<0 && pCons->op==SQLITE_INDEX_CONSTRAINT_EQ && bDocid ){ ADDED test/fts3join.test Index: test/fts3join.test ================================================================== --- /dev/null +++ test/fts3join.test @@ -0,0 +1,66 @@ +# 2014 January 4 +# +# 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 FTS3 module. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set ::testprefix fts3join + +# If SQLITE_ENABLE_FTS3 is defined, omit this file. +ifcapable !fts3 { + finish_test + return +} + +do_execsql_test 1.0 { + CREATE VIRTUAL TABLE ft1 USING fts4(x); + INSERT INTO ft1 VALUES('aaa aaa'); + INSERT INTO ft1 VALUES('aaa bbb'); + INSERT INTO ft1 VALUES('bbb aaa'); + INSERT INTO ft1 VALUES('bbb bbb'); + + CREATE TABLE t1(id, y); + INSERT INTO t1 VALUES(1, 'aaa'); + INSERT INTO t1 VALUES(2, 'bbb'); +} + +do_execsql_test 1.1 { + SELECT docid FROM ft1, t1 WHERE ft1 MATCH y AND id=1; +} {1 2 3} + +do_execsql_test 1.2 { + SELECT docid FROM ft1, t1 WHERE ft1 MATCH y AND id=1 ORDER BY docid; +} {1 2 3} + +do_execsql_test 2.0 { + CREATE VIRTUAL TABLE ft2 USING fts4(x); + CREATE VIRTUAL TABLE ft3 USING fts4(y); + + INSERT INTO ft2 VALUES('abc'); + INSERT INTO ft2 VALUES('def'); + INSERT INTO ft3 VALUES('ghi'); + INSERT INTO ft3 VALUES('abc'); +} + +do_execsql_test 2.1 { SELECT * FROM ft2, ft3 WHERE x MATCH y; } {abc abc} +do_execsql_test 2.2 { SELECT * FROM ft2, ft3 WHERE y MATCH x; } {abc abc} +do_execsql_test 2.3 { SELECT * FROM ft3, ft2 WHERE x MATCH y; } {abc abc} +do_execsql_test 2.4 { SELECT * FROM ft3, ft2 WHERE y MATCH x; } {abc abc} + +do_catchsql_test 2.5 { + SELECT * FROM ft3, ft2 WHERE y MATCH x AND x MATCH y; +} {1 {unable to use function MATCH in the requested context}} + +finish_test + +