mirror of
https://github.com/sqlite/sqlite.git
synced 2025-11-19 21:43:15 +03:00
When the WHERE clause contains two OR-connected terms with identical
operands but different operators, try to combine them into a single term. Example: (X=A OR X>A) becomes (X>=A). FossilOrigin-Name: 8bdda827a3d268009297a0216e3d94bf0eceeb2e
This commit is contained in:
14
manifest
14
manifest
@@ -1,5 +1,5 @@
|
|||||||
C Modify\sthe\sfts3query.test\sscript\sso\sthat\sit\sworks\seven\swhen\stestfixture\sis\nbuilt\susing\sa\sversion\sof\sTCL\sthat\sis\sunable\sto\ssort\sthe\ninteger\s-9223372036854775808
|
C When\sthe\sWHERE\sclause\scontains\stwo\sOR-connected\sterms\swith\sidentical\noperands\sbut\sdifferent\soperators,\stry\sto\scombine\sthem\sinto\sa\ssingle\sterm.\nExample:\s\s(X=A\sOR\sX>A)\sbecomes\s(X>=A).
|
||||||
D 2015-03-16T17:07:09.229
|
D 2015-03-16T17:48:12.769
|
||||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||||
F Makefile.in 88a3e6261286db378fdffa1124cad11b3c05f5bb
|
F Makefile.in 88a3e6261286db378fdffa1124cad11b3c05f5bb
|
||||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||||
@@ -307,7 +307,7 @@ F src/vxworks.h c18586c8edc1bddbc15c004fa16aeb1e1342b4fb
|
|||||||
F src/wal.c 39303f2c9db02a4e422cd8eb2c8760420c6a51fe
|
F src/wal.c 39303f2c9db02a4e422cd8eb2c8760420c6a51fe
|
||||||
F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4
|
F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4
|
||||||
F src/walker.c c253b95b4ee44b21c406e2a1052636c31ea27804
|
F src/walker.c c253b95b4ee44b21c406e2a1052636c31ea27804
|
||||||
F src/where.c 5a4e4ab378dbddeca59ad283c61aa67c6e56a913
|
F src/where.c 5ca2899f280c4259a07816f4395d0bea368cfbd7
|
||||||
F src/whereInt.h cbe4aa57326998d89e7698ca65bb7c28541d483c
|
F src/whereInt.h cbe4aa57326998d89e7698ca65bb7c28541d483c
|
||||||
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
|
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
|
||||||
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
|
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
|
||||||
@@ -1175,6 +1175,7 @@ F test/whereG.test 69f5ec4b15760a8c860f80e2d55525669390aab3
|
|||||||
F test/whereH.test e4b07f7a3c2f5d31195cd33710054c78667573b2
|
F test/whereH.test e4b07f7a3c2f5d31195cd33710054c78667573b2
|
||||||
F test/whereI.test 1d89199697919d4930be05a71e7fe620f114e622
|
F test/whereI.test 1d89199697919d4930be05a71e7fe620f114e622
|
||||||
F test/whereJ.test 55a3221706a7ab706293f17cc8f96da563bf0767
|
F test/whereJ.test 55a3221706a7ab706293f17cc8f96da563bf0767
|
||||||
|
F test/whereK.test 78fb50c74c9a91efc97a1daa39bcf7b8b68d8bff
|
||||||
F test/wherelimit.test 5e9fd41e79bb2b2d588ed999d641d9c965619b31
|
F test/wherelimit.test 5e9fd41e79bb2b2d588ed999d641d9c965619b31
|
||||||
F test/wild001.test bca33f499866f04c24510d74baf1e578d4e44b1c
|
F test/wild001.test bca33f499866f04c24510d74baf1e578d4e44b1c
|
||||||
F test/win32heap.test ea19770974795cff26e11575e12d422dbd16893c
|
F test/win32heap.test ea19770974795cff26e11575e12d422dbd16893c
|
||||||
@@ -1244,7 +1245,8 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
|
|||||||
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
|
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
|
||||||
F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
|
F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
|
||||||
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
|
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
|
||||||
P 018d7671402a0f8103d1306641655b69f9fa235d
|
P f61fd24b4d3b686911ea578f77612309099f0cc6 23f71a26386ff2aff9800fe96cec1dc9c805b5b6
|
||||||
R 781e204d7688b0ad2593ede4825cc937
|
R 0810c9b992d63ac661e729a83d3536b4
|
||||||
|
T +closed 23f71a26386ff2aff9800fe96cec1dc9c805b5b6
|
||||||
U drh
|
U drh
|
||||||
Z 1bc9733af443d0b92b43469552bb99df
|
Z f335a12b6cd889ae802417698f792d1a
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
f61fd24b4d3b686911ea578f77612309099f0cc6
|
8bdda827a3d268009297a0216e3d94bf0eceeb2e
|
||||||
102
src/where.c
102
src/where.c
@@ -770,6 +770,79 @@ static void markTermAsChild(WhereClause *pWC, int iChild, int iParent){
|
|||||||
pWC->a[iParent].nChild++;
|
pWC->a[iParent].nChild++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Return the N-th AND-connected subterm of pTerm. Or if pTerm is not
|
||||||
|
** a conjunction, then return just pTerm when N==0. If N is exceeds
|
||||||
|
** the number of available subterms, return NULL.
|
||||||
|
*/
|
||||||
|
static WhereTerm *whereNthSubterm(WhereTerm *pTerm, int N){
|
||||||
|
if( pTerm->eOperator!=WO_AND ){
|
||||||
|
return N==0 ? pTerm : 0;
|
||||||
|
}
|
||||||
|
if( N<pTerm->u.pAndInfo->wc.nTerm ){
|
||||||
|
return &pTerm->u.pAndInfo->wc.a[N];
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Subterms pOne and pTwo are contained within WHERE clause pWC. The
|
||||||
|
** two subterms are in disjunction - they are OR-ed together.
|
||||||
|
**
|
||||||
|
** If these two terms are both of the form: "A op B" with the same
|
||||||
|
** A and B values but different operators and if the operators are
|
||||||
|
** compatible (if one is = and the other is <, for example) then
|
||||||
|
** add a new virtual AND term to pWC that is the combination of the
|
||||||
|
** two.
|
||||||
|
**
|
||||||
|
** Some examples:
|
||||||
|
**
|
||||||
|
** x<y OR x=y --> x<=y
|
||||||
|
** x=y OR x=y --> x=y
|
||||||
|
** x<=y OR x<y --> x<=y
|
||||||
|
**
|
||||||
|
** The following is NOT generated:
|
||||||
|
**
|
||||||
|
** x<y OR x>y --> x!=y
|
||||||
|
*/
|
||||||
|
static void whereCombineDisjuncts(
|
||||||
|
SrcList *pSrc, /* the FROM clause */
|
||||||
|
WhereClause *pWC, /* The complete WHERE clause */
|
||||||
|
WhereTerm *pOne, /* First disjunct */
|
||||||
|
WhereTerm *pTwo /* Second disjunct */
|
||||||
|
){
|
||||||
|
u16 eOp = pOne->eOperator | pTwo->eOperator;
|
||||||
|
sqlite3 *db; /* Database connection (for malloc) */
|
||||||
|
Expr *pNew; /* New virtual expression */
|
||||||
|
int op; /* Operator for the combined expression */
|
||||||
|
int idxNew; /* Index in pWC of the next virtual term */
|
||||||
|
|
||||||
|
if( (pOne->eOperator & (WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE))==0 ) return;
|
||||||
|
if( (pTwo->eOperator & (WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE))==0 ) return;
|
||||||
|
if( (eOp & (WO_EQ|WO_LT|WO_LE))!=eOp
|
||||||
|
&& (eOp & (WO_EQ|WO_GT|WO_GE))!=eOp ) return;
|
||||||
|
assert( pOne->pExpr->pLeft!=0 && pOne->pExpr->pRight!=0 );
|
||||||
|
assert( pTwo->pExpr->pLeft!=0 && pTwo->pExpr->pRight!=0 );
|
||||||
|
if( sqlite3ExprCompare(pOne->pExpr->pLeft, pTwo->pExpr->pLeft, -1) ) return;
|
||||||
|
if( sqlite3ExprCompare(pOne->pExpr->pRight, pTwo->pExpr->pRight, -1) )return;
|
||||||
|
/* If we reach this point, it means the two subterms can be combined */
|
||||||
|
if( (eOp & (eOp-1))!=0 ){
|
||||||
|
if( eOp & (WO_LT|WO_LE) ){
|
||||||
|
eOp = WO_LE;
|
||||||
|
}else{
|
||||||
|
assert( eOp & (WO_GT|WO_GE) );
|
||||||
|
eOp = WO_GE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
db = pWC->pWInfo->pParse->db;
|
||||||
|
pNew = sqlite3ExprDup(db, pOne->pExpr, 0);
|
||||||
|
if( pNew==0 ) return;
|
||||||
|
for(op=TK_EQ; eOp!=(WO_EQ<<(op-TK_EQ)); op++){ assert( op<TK_GE ); }
|
||||||
|
pNew->op = op;
|
||||||
|
idxNew = whereClauseInsert(pWC, pNew, TERM_VIRTUAL|TERM_DYNAMIC);
|
||||||
|
exprAnalyze(pSrc, pWC, idxNew);
|
||||||
|
}
|
||||||
|
|
||||||
#if !defined(SQLITE_OMIT_OR_OPTIMIZATION) && !defined(SQLITE_OMIT_SUBQUERY)
|
#if !defined(SQLITE_OMIT_OR_OPTIMIZATION) && !defined(SQLITE_OMIT_SUBQUERY)
|
||||||
/*
|
/*
|
||||||
** Analyze a term that consists of two or more OR-connected
|
** Analyze a term that consists of two or more OR-connected
|
||||||
@@ -794,6 +867,7 @@ static void markTermAsChild(WhereClause *pWC, int iChild, int iParent){
|
|||||||
** (C) t1.x=t2.y OR (t1.x=t2.z AND t1.y=15)
|
** (C) t1.x=t2.y OR (t1.x=t2.z AND t1.y=15)
|
||||||
** (D) x=expr1 OR (y>11 AND y<22 AND z LIKE '*hello*')
|
** (D) x=expr1 OR (y>11 AND y<22 AND z LIKE '*hello*')
|
||||||
** (E) (p.a=1 AND q.b=2 AND r.c=3) OR (p.x=4 AND q.y=5 AND r.z=6)
|
** (E) (p.a=1 AND q.b=2 AND r.c=3) OR (p.x=4 AND q.y=5 AND r.z=6)
|
||||||
|
** (F) x>A OR (x=A AND y>=B)
|
||||||
**
|
**
|
||||||
** CASE 1:
|
** CASE 1:
|
||||||
**
|
**
|
||||||
@@ -810,6 +884,16 @@ static void markTermAsChild(WhereClause *pWC, int iChild, int iParent){
|
|||||||
**
|
**
|
||||||
** CASE 2:
|
** CASE 2:
|
||||||
**
|
**
|
||||||
|
** If there are exactly two disjuncts one side has x>A and the other side
|
||||||
|
** has x=A (for the same x and A) then add a new virtual conjunct term to the
|
||||||
|
** WHERE clause of the form "x>=A". Example:
|
||||||
|
**
|
||||||
|
** x>A OR (x=A AND y>B) adds: x>=A
|
||||||
|
**
|
||||||
|
** The added conjunct can sometimes be helpful in query planning.
|
||||||
|
**
|
||||||
|
** CASE 3:
|
||||||
|
**
|
||||||
** If all subterms are indexable by a single table T, then set
|
** If all subterms are indexable by a single table T, then set
|
||||||
**
|
**
|
||||||
** WhereTerm.eOperator = WO_OR
|
** WhereTerm.eOperator = WO_OR
|
||||||
@@ -936,12 +1020,26 @@ static void exprAnalyzeOrTerm(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Record the set of tables that satisfy case 2. The set might be
|
** Record the set of tables that satisfy case 3. The set might be
|
||||||
** empty.
|
** empty.
|
||||||
*/
|
*/
|
||||||
pOrInfo->indexable = indexable;
|
pOrInfo->indexable = indexable;
|
||||||
pTerm->eOperator = indexable==0 ? 0 : WO_OR;
|
pTerm->eOperator = indexable==0 ? 0 : WO_OR;
|
||||||
|
|
||||||
|
/* For a two-way OR, attempt to implementation case 2.
|
||||||
|
*/
|
||||||
|
if( indexable && pOrWc->nTerm==2 ){
|
||||||
|
int iOne = 0;
|
||||||
|
WhereTerm *pOne;
|
||||||
|
while( (pOne = whereNthSubterm(&pOrWc->a[0],iOne++))!=0 ){
|
||||||
|
int iTwo = 0;
|
||||||
|
WhereTerm *pTwo;
|
||||||
|
while( (pTwo = whereNthSubterm(&pOrWc->a[1],iTwo++))!=0 ){
|
||||||
|
whereCombineDisjuncts(pSrc, pWC, pOne, pTwo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** chngToIN holds a set of tables that *might* satisfy case 1. But
|
** chngToIN holds a set of tables that *might* satisfy case 1. But
|
||||||
** we have to do some additional checking to see if case 1 really
|
** we have to do some additional checking to see if case 1 really
|
||||||
@@ -1071,7 +1169,7 @@ static void exprAnalyzeOrTerm(
|
|||||||
}else{
|
}else{
|
||||||
sqlite3ExprListDelete(db, pList);
|
sqlite3ExprListDelete(db, pList);
|
||||||
}
|
}
|
||||||
pTerm->eOperator = WO_NOOP; /* case 1 trumps case 2 */
|
pTerm->eOperator = WO_NOOP; /* case 1 trumps case 3 */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
72
test/whereK.test
Normal file
72
test/whereK.test
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
# 2015-03-16
|
||||||
|
#
|
||||||
|
# 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 file is testing OR expressions where terms can be
|
||||||
|
# factored from either side of the OR and combined into a single new
|
||||||
|
# AND term that is beneficial to the search. Examples:
|
||||||
|
#
|
||||||
|
# (x>A OR x=A) --> ... AND (x>=A)
|
||||||
|
# (x>A OR (x=A AND y>=B) --> ... AND (x>=A)
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
set testdir [file dirname $argv0]
|
||||||
|
source $testdir/tester.tcl
|
||||||
|
set ::testprefix whereD
|
||||||
|
|
||||||
|
do_execsql_test 1.1 {
|
||||||
|
CREATE TABLE t1(a,b,c);
|
||||||
|
WITH RECURSIVE c(x) AS (VALUES(0) UNION ALL SELECT x+1 FROM c WHERE x<99)
|
||||||
|
INSERT INTO t1(a,b,c) SELECT x, x/10, x%10 FROM c;
|
||||||
|
CREATE INDEX t1bc ON t1(b,c);
|
||||||
|
SELECT a FROM t1 WHERE b>9 OR b=9 ORDER BY +a;
|
||||||
|
} {90 91 92 93 94 95 96 97 98 99}
|
||||||
|
do_execsql_test 1.1eqp {
|
||||||
|
EXPLAIN QUERY PLAN
|
||||||
|
SELECT a FROM t1 WHERE b>9 OR b=9 ORDER BY +a;
|
||||||
|
} {/SEARCH TABLE t1 USING INDEX t1bc/}
|
||||||
|
|
||||||
|
do_execsql_test 1.2 {
|
||||||
|
SELECT a FROM t1 WHERE b>8 OR (b=8 AND c>7) ORDER BY +a;
|
||||||
|
} {88 89 90 91 92 93 94 95 96 97 98 99}
|
||||||
|
do_execsql_test 1.2eqp {
|
||||||
|
EXPLAIN QUERY PLAN
|
||||||
|
SELECT a FROM t1 WHERE b>8 OR (b=8 AND c>7) ORDER BY +a;
|
||||||
|
} {/SEARCH TABLE t1 USING INDEX t1bc/}
|
||||||
|
|
||||||
|
do_execsql_test 1.3 {
|
||||||
|
SELECT a FROM t1 WHERE (b=8 AND c>7) OR b>8 ORDER BY +a;
|
||||||
|
} {88 89 90 91 92 93 94 95 96 97 98 99}
|
||||||
|
do_execsql_test 1.3eqp {
|
||||||
|
EXPLAIN QUERY PLAN
|
||||||
|
SELECT a FROM t1 WHERE (b=8 AND c>7) OR b>8 ORDER BY +a;
|
||||||
|
} {/SEARCH TABLE t1 USING INDEX t1bc/}
|
||||||
|
|
||||||
|
do_execsql_test 1.4 {
|
||||||
|
SELECT a FROM t1 WHERE (b=8 AND c>7) OR 8<b ORDER BY +a;
|
||||||
|
} {88 89 90 91 92 93 94 95 96 97 98 99}
|
||||||
|
do_execsql_test 1.4eqp {
|
||||||
|
EXPLAIN QUERY PLAN
|
||||||
|
SELECT a FROM t1 WHERE (b=8 AND c>7) OR 8<b ORDER BY +a;
|
||||||
|
} {/SEARCH TABLE t1 USING INDEX t1bc/}
|
||||||
|
|
||||||
|
do_execsql_test 1.5 {
|
||||||
|
SELECT a FROM t1 WHERE (b=8 AND c>7) OR (b>8 AND c NOT IN (4,5,6))
|
||||||
|
ORDER BY +a;
|
||||||
|
} {88 89 90 91 92 93 97 98 99}
|
||||||
|
do_execsql_test 1.5eqp {
|
||||||
|
EXPLAIN QUERY PLAN
|
||||||
|
SELECT a FROM t1 WHERE (b=8 AND c>7) OR (b>8 AND c NOT IN (4,5,6))
|
||||||
|
ORDER BY +a;
|
||||||
|
} {/SEARCH TABLE t1 USING INDEX t1bc/}
|
||||||
|
|
||||||
|
finish_test
|
||||||
Reference in New Issue
Block a user