Index: ext/fts3/fts3.c ================================================================== --- ext/fts3/fts3.c +++ ext/fts3/fts3.c @@ -3560,22 +3560,20 @@ sqlite3_context *pContext, /* SQL function call context */ const char *zFunc, /* Function name */ sqlite3_value *pVal, /* argv[0] passed to function */ Fts3Cursor **ppCsr /* OUT: Store cursor handle here */ ){ - Fts3Cursor *pRet; - if( sqlite3_value_type(pVal)!=SQLITE_BLOB - || sqlite3_value_bytes(pVal)!=sizeof(Fts3Cursor *) - ){ + int rc = SQLITE_OK; + if( sqlite3_value_subtype(pVal)==SQLITE_BLOB ){ + *ppCsr = *(Fts3Cursor**)sqlite3_value_blob(pVal); + }else{ char *zErr = sqlite3_mprintf("illegal first argument to %s", zFunc); sqlite3_result_error(pContext, zErr, -1); sqlite3_free(zErr); - return SQLITE_ERROR; + rc = SQLITE_ERROR; } - memcpy(&pRet, sqlite3_value_blob(pVal), sizeof(Fts3Cursor *)); - *ppCsr = pRet; - return SQLITE_OK; + return rc; } /* ** Implementation of the snippet() function for FTS3 */ @@ -5289,55 +5287,51 @@ ** left-hand child may be either a phrase or a NEAR node. There are ** no exceptions to this - it's the way the parser in fts3_expr.c works. */ if( *pRc==SQLITE_OK && pExpr->eType==FTSQUERY_NEAR - && pExpr->bEof==0 && (pExpr->pParent==0 || pExpr->pParent->eType!=FTSQUERY_NEAR) ){ Fts3Expr *p; int nTmp = 0; /* Bytes of temp space */ char *aTmp; /* Temp space for PoslistNearMerge() */ /* Allocate temporary working space. */ for(p=pExpr; p->pLeft; p=p->pLeft){ + assert( p->pRight->pPhrase->doclist.nList>0 ); nTmp += p->pRight->pPhrase->doclist.nList; } nTmp += p->pPhrase->doclist.nList; - if( nTmp==0 ){ - res = 0; - }else{ - aTmp = sqlite3_malloc(nTmp*2); - if( !aTmp ){ - *pRc = SQLITE_NOMEM; - res = 0; - }else{ - char *aPoslist = p->pPhrase->doclist.pList; - int nToken = p->pPhrase->nToken; - - for(p=p->pParent;res && p && p->eType==FTSQUERY_NEAR; p=p->pParent){ - Fts3Phrase *pPhrase = p->pRight->pPhrase; - int nNear = p->nNear; - res = fts3EvalNearTrim(nNear, aTmp, &aPoslist, &nToken, pPhrase); - } - - aPoslist = pExpr->pRight->pPhrase->doclist.pList; - nToken = pExpr->pRight->pPhrase->nToken; - for(p=pExpr->pLeft; p && res; p=p->pLeft){ - int nNear; - Fts3Phrase *pPhrase; - assert( p->pParent && p->pParent->pLeft==p ); - nNear = p->pParent->nNear; - pPhrase = ( - p->eType==FTSQUERY_NEAR ? p->pRight->pPhrase : p->pPhrase - ); - res = fts3EvalNearTrim(nNear, aTmp, &aPoslist, &nToken, pPhrase); - } - } - - sqlite3_free(aTmp); - } + aTmp = sqlite3_malloc(nTmp*2); + if( !aTmp ){ + *pRc = SQLITE_NOMEM; + res = 0; + }else{ + char *aPoslist = p->pPhrase->doclist.pList; + int nToken = p->pPhrase->nToken; + + for(p=p->pParent;res && p && p->eType==FTSQUERY_NEAR; p=p->pParent){ + Fts3Phrase *pPhrase = p->pRight->pPhrase; + int nNear = p->nNear; + res = fts3EvalNearTrim(nNear, aTmp, &aPoslist, &nToken, pPhrase); + } + + aPoslist = pExpr->pRight->pPhrase->doclist.pList; + nToken = pExpr->pRight->pPhrase->nToken; + for(p=pExpr->pLeft; p && res; p=p->pLeft){ + int nNear; + Fts3Phrase *pPhrase; + assert( p->pParent && p->pParent->pLeft==p ); + nNear = p->pParent->nNear; + pPhrase = ( + p->eType==FTSQUERY_NEAR ? p->pRight->pPhrase : p->pPhrase + ); + res = fts3EvalNearTrim(nNear, aTmp, &aPoslist, &nToken, pPhrase); + } + } + + sqlite3_free(aTmp); } return res; } Index: test/fts3misc.test ================================================================== --- test/fts3misc.test +++ test/fts3misc.test @@ -169,8 +169,60 @@ UPDATE t4_stat SET value = X'00C03EC0B204C0A608' WHERE id=0; } do_catchsql_test 4.6 { SELECT count(*) FROM t4 WHERE t4 MATCH '"a b c" OR "c a b"' } {1 {database disk image is malformed}} + +#------------------------------------------------------------------------- +# +reset_db +do_execsql_test 5.0 { + CREATE VIRTUAL TABLE t5 USING fts4; + INSERT INTO t5 VALUES('a x x x x b x x x x c'); + INSERT INTO t5 VALUES('a x x x x b x x x x c'); + INSERT INTO t5 VALUES('a x x x x b x x x x c'); +} +do_execsql_test 5.1 { + SELECT rowid FROM t5 WHERE t5 MATCH 'a NEAR/4 b NEAR/4 c' +} {1 2 3} +do_execsql_test 5.2 { + SELECT rowid FROM t5 WHERE t5 MATCH 'a NEAR/3 b NEAR/4 c' +} {} +do_execsql_test 5.3 { + SELECT rowid FROM t5 WHERE t5 MATCH 'a NEAR/4 b NEAR/3 c' +} {} +do_execsql_test 5.4 { + SELECT rowid FROM t5 WHERE t5 MATCH 'y NEAR/4 b NEAR/4 c' +} {} +do_execsql_test 5.5 { + SELECT rowid FROM t5 WHERE t5 MATCH 'x OR a NEAR/3 b NEAR/3 c' +} {1 2 3} +do_execsql_test 5.5 { + SELECT rowid FROM t5 WHERE t5 MATCH 'x OR y NEAR/3 b NEAR/3 c' +} {1 2 3} + +#------------------------------------------------------------------------- +# +reset_db +do_execsql_test 6.0 { + CREATE VIRTUAL TABLE t6 USING fts4; + + BEGIN; + WITH s(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<50000) + INSERT INTO t6 SELECT 'x x x x x x x x x x x' FROM s; + + INSERT INTO t6 VALUES('x x x x x x x x x x x A'); + INSERT INTO t6 VALUES('x x x x x x x x x x x B'); + INSERT INTO t6 VALUES('x x x x x x x x x x x A'); + INSERT INTO t6 VALUES('x x x x x x x x x x x B'); + + WITH s(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<50000) + INSERT INTO t6 SELECT 'x x x x x x x x x x x' FROM s; + COMMIT; +} +breakpoint +do_execsql_test 6.1 { + SELECT rowid FROM t6 WHERE t6 MATCH 'b OR "x a"' +} {50001 50002 50003 50004} finish_test