Initial implementation of the WHERE-clause constant propagation optimization.
FossilOrigin-Name: 2fb82ad8ebb6434438c0d235b1239444fb08c8711cea2c5a9ed955fedd0acdec
This commit is contained in:
parent
42b2653871
commit
660ee55663
20
manifest
20
manifest
@ -1,5 +1,5 @@
|
||||
C Fix\sa\sbuffer\soverread\sin\sfts5.
|
||||
D 2018-07-25T15:25:55.074
|
||||
C Initial\simplementation\sof\sthe\sWHERE-clause\sconstant\spropagation\soptimization.
|
||||
D 2018-07-26T21:16:53.742
|
||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||
F Makefile.in 0a3a6c81e6fcb969ff9106e882f0a08547014ba463cb6beca4c4efaecc924ee6
|
||||
@ -498,12 +498,12 @@ F src/printf.c 7f6f3cba8e0c49c19e30a1ff4e9aeda6e06814dcbad4b664a69e1b6cb6e7e365
|
||||
F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
|
||||
F src/resolve.c 797088662ed61102485e3070ba3b3f7828bd5ef6a588223ba6865d77d52f6cea
|
||||
F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac
|
||||
F src/select.c 2e9661d4424f43ccf595c4a7b4acdf32db523c0f6b31cbd62e6e5a2f43118981
|
||||
F src/select.c cd3993844ab1186e80069f9de2304e730732cfea1f718e603293792d4401d037
|
||||
F src/shell.c.in f6ebd05c461805a7c708333cd645e74e0a93560d2118f5adb73a75d8c9cf6b01
|
||||
F src/sqlite.h.in c6451bb876adced3aba5b1682c6317d215c5eceaba21a6ce979e71a0b8d0bf95
|
||||
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
||||
F src/sqlite3ext.h 9887b27e69c01e79c2cbe74ef73bf01af5b5703d6a7f0a4371e386d7249cb1c7
|
||||
F src/sqliteInt.h 427471586351daefbc81155384a702b2173801051fe345bc25a09558b919db77
|
||||
F src/sqliteInt.h 0da0d929642cacf99963e406d34e2e3be891a30168f99aefc1cb81c2c545a09b
|
||||
F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b
|
||||
F src/status.c 46e7aec11f79dad50965a5ca5fa9de009f7d6bde08be2156f1538a0a296d4d0e
|
||||
F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34
|
||||
@ -1617,6 +1617,7 @@ F test/whereH.test e4b07f7a3c2f5d31195cd33710054c78667573b2
|
||||
F test/whereI.test b7769ee8dbefd987fb266715fee887f05f9ff180016b06fca7fa402df739193b
|
||||
F test/whereJ.test 88287550f6ee604422403b053455b1ad894eeaa5c35d348532dfa1439286cb9a
|
||||
F test/whereK.test f8e3cf26a8513ecc7f514f54df9f0572c046c42b
|
||||
F test/whereL.test 46492cf4b3b2bfa5bb8adb8d6565f31c010bf8f23baf66bcba4e38cec89117a2
|
||||
F test/wherefault.test 1374c3aa198388925246475f84ad4cd5f9528864
|
||||
F test/wherelfault.test 9012e4ef5259058b771606616bd007af5d154e64cc25fa9fd4170f6411db44e3
|
||||
F test/wherelimit.test 592081800806d297dd7449b1030c863d2883d6d42901837ccd2e5a9bd962edb0
|
||||
@ -1752,7 +1753,10 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||
P 6ee2950b272ede475e485bfaa7d413eaa81482fe9dd6452aeeaf95ff7938f7da
|
||||
R b452b7474ada2f7df230e94f47eaea02
|
||||
U dan
|
||||
Z 3eecc6d8fc02fe02d8a834ac76aa6a16
|
||||
P 0e3de8abbb0c7ae64e637776cb055ce79736f99a103e00e44d17a6b091b98c81
|
||||
R 83557e363f35588e2f481ff35d0d8054
|
||||
T *branch * propagate-const-opt
|
||||
T *sym-propagate-const-opt *
|
||||
T -sym-trunk *
|
||||
U drh
|
||||
Z 8488ca3ac81dbe859bc19508bb265e68
|
||||
|
@ -1 +1 @@
|
||||
0e3de8abbb0c7ae64e637776cb055ce79736f99a103e00e44d17a6b091b98c81
|
||||
2fb82ad8ebb6434438c0d235b1239444fb08c8711cea2c5a9ed955fedd0acdec
|
141
src/select.c
141
src/select.c
@ -4074,7 +4074,134 @@ static int flattenSubquery(
|
||||
}
|
||||
#endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */
|
||||
|
||||
/*
|
||||
** A structure to keep track of all of the column values that must be
|
||||
** constant in a WHERE clause.
|
||||
*/
|
||||
typedef struct WhereConst WhereConst;
|
||||
struct WhereConst {
|
||||
sqlite3 *db; /* Database pointer, used by sqlite3DbRealloc() */
|
||||
int nConst; /* Number for COLUMN=CONSTANT terms */
|
||||
int nChng; /* Number of times a constant is propagated */
|
||||
Expr **apExpr; /* [i*2] is COLUMN and [i*2+1] is CONSTANT */
|
||||
};
|
||||
|
||||
/*
|
||||
** Add a new entry to the pConst object
|
||||
*/
|
||||
static void constInsert(
|
||||
WhereConst *pConst,
|
||||
Expr *pColumn,
|
||||
Expr *pValue
|
||||
){
|
||||
pConst->nConst++;
|
||||
pConst->apExpr = sqlite3DbReallocOrFree(pConst->db, pConst->apExpr,
|
||||
pConst->nConst*2*sizeof(Expr*));
|
||||
if( pConst->apExpr==0 ){
|
||||
pConst->nConst = 0;
|
||||
}else{
|
||||
pConst->apExpr[pConst->nConst*2-2] = pColumn;
|
||||
pConst->apExpr[pConst->nConst*2-1] = pValue;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Find all instances of COLUMN=CONSTANT or CONSTANT=COLUMN in pExpr that
|
||||
** must be true (that are part of the AND-connected terms) and add each
|
||||
** to pConst.
|
||||
*/
|
||||
static void findConstInWhere(WhereConst *pConst, Expr *pExpr){
|
||||
if( pExpr==0 ) return;
|
||||
if( ExprHasProperty(pExpr, EP_FromJoin) ) return;
|
||||
if( pExpr->op==TK_AND ){
|
||||
findConstInWhere(pConst, pExpr->pRight);
|
||||
findConstInWhere(pConst, pExpr->pLeft);
|
||||
return;
|
||||
}
|
||||
if( pExpr->op!=TK_EQ ) return;
|
||||
assert( pExpr->pRight!=0 );
|
||||
assert( pExpr->pLeft!=0 );
|
||||
if( pExpr->pRight->op==TK_COLUMN && sqlite3ExprIsConstant(pExpr->pLeft) ){
|
||||
constInsert(pConst, pExpr->pRight, pExpr->pLeft);
|
||||
}else
|
||||
if( pExpr->pLeft->op==TK_COLUMN && sqlite3ExprIsConstant(pExpr->pRight) ){
|
||||
constInsert(pConst, pExpr->pLeft, pExpr->pRight);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** This is a Walker expression callback. pExpr is a candidate expression
|
||||
** to be replaced by a value. If pExpr is equivalent to one of the
|
||||
** columns named in pWalker->u.pConst, then overwrite it with its
|
||||
** corresponding value.
|
||||
*/
|
||||
static int propagateConstantExprRewrite(Walker *pWalker, Expr *pExpr){
|
||||
int i;
|
||||
WhereConst *pConst;
|
||||
if( pExpr->op!=TK_COLUMN ) return WRC_Continue;
|
||||
pConst = pWalker->u.pConst;
|
||||
for(i=0; i<pConst->nConst; i++){
|
||||
Expr *pColumn = pConst->apExpr[i*2];
|
||||
if( pColumn==pExpr ) continue;
|
||||
if( pColumn->iTable!=pExpr->iTable ) continue;
|
||||
if( pColumn->iColumn!=pExpr->iColumn ) continue;
|
||||
/* A match is found. Transform the COLUMN into a CONSTANT */
|
||||
pConst->nChng++;
|
||||
ExprClearProperty(pExpr, EP_Leaf);
|
||||
pExpr->op = TK_UPLUS;
|
||||
pExpr->pLeft = sqlite3ExprDup(pConst->db, pConst->apExpr[i*2+1], 0);
|
||||
break;
|
||||
}
|
||||
return WRC_Prune;
|
||||
}
|
||||
|
||||
/*
|
||||
** The WHERE-clause constant propagation optimization.
|
||||
**
|
||||
** If the WHERE clause contains terms of the form COLUMN=CONSTANT or
|
||||
** CONSTANT=COLUMN that must be tree (in other words, if the terms top-level
|
||||
** AND-connected terms that are not part of a ON clause from a LEFT JOIN)
|
||||
** then throughout the query replace all other occurrences of COLUMN
|
||||
** with CONSTANT.
|
||||
**
|
||||
** For example, the query:
|
||||
**
|
||||
** SELECT * FROM t1, t2, t3 WHERE t1.a=39 AND t2.b=t1.a AND t3.c=t2.b
|
||||
**
|
||||
** Is transformed into
|
||||
**
|
||||
** SELECT * FROM t1, t2, t3 WHERE t1.a=39 AND t2.b=39 AND t3.c=39
|
||||
**
|
||||
** Return true if any transformations where made and false if not.
|
||||
*/
|
||||
static int propagateConstants(
|
||||
Parse *pParse, /* The parsing context */
|
||||
Select *p /* The query in which to propagate constants */
|
||||
){
|
||||
WhereConst x;
|
||||
Walker w;
|
||||
int nChng = 0;
|
||||
x.db = pParse->db;
|
||||
do{
|
||||
x.nConst = 0;
|
||||
x.nChng = 0;
|
||||
x.apExpr = 0;
|
||||
findConstInWhere(&x, p->pWhere);
|
||||
if( x.nConst ){
|
||||
memset(&w, 0, sizeof(w));
|
||||
w.pParse = pParse;
|
||||
w.xExprCallback = propagateConstantExprRewrite;
|
||||
w.xSelectCallback = sqlite3SelectWalkNoop;
|
||||
w.xSelectCallback2 = 0;
|
||||
w.walkerDepth = 0;
|
||||
w.u.pConst = &x;
|
||||
sqlite3WalkSelect(&w, p);
|
||||
sqlite3DbFree(x.db, x.apExpr);
|
||||
nChng += x.nChng;
|
||||
}
|
||||
}while( x.nChng );
|
||||
return nChng;
|
||||
}
|
||||
|
||||
#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
|
||||
/*
|
||||
@ -5663,6 +5790,20 @@ int sqlite3Select(
|
||||
*/
|
||||
pParse->nHeight += sqlite3SelectExprHeight(p);
|
||||
|
||||
/* Do the constant propagation optimization */
|
||||
if( OptimizationEnabled(db, SQLITE_PropagateConst)
|
||||
&& propagateConstants(pParse, p)
|
||||
){
|
||||
#if SELECTTRACE_ENABLED
|
||||
if( sqlite3SelectTrace & 0x100 ){
|
||||
SELECTTRACE(0x100,pParse,p,("After constant propagation:\n"));
|
||||
sqlite3TreeViewSelect(0, p, 0);
|
||||
}
|
||||
#endif
|
||||
}else{
|
||||
SELECTTRACE(0x100,pParse,p,("Constant propagation not possible\n"));
|
||||
}
|
||||
|
||||
/* Make copies of constant WHERE-clause terms in the outer query down
|
||||
** inside the subquery. This can help the subquery to run more efficiently.
|
||||
*/
|
||||
|
@ -1584,6 +1584,7 @@ struct sqlite3 {
|
||||
#define SQLITE_PushDown 0x1000 /* The push-down optimization */
|
||||
#define SQLITE_SimplifyJoin 0x2000 /* Convert LEFT JOIN to JOIN */
|
||||
#define SQLITE_SkipScan 0x4000 /* Skip-scans */
|
||||
#define SQLITE_PropagateConst 0x8000 /* The constant propagation opt */
|
||||
#define SQLITE_AllOpts 0xffff /* All optimizations */
|
||||
|
||||
/*
|
||||
@ -3436,6 +3437,7 @@ struct Walker {
|
||||
ExprList *pGroupBy; /* GROUP BY clause */
|
||||
Select *pSelect; /* HAVING to WHERE clause ctx */
|
||||
struct WindowRewrite *pRewrite; /* Window rewrite context */
|
||||
struct WhereConst *pConst; /* WHERE clause constants */
|
||||
} u;
|
||||
};
|
||||
|
||||
|
39
test/whereL.test
Normal file
39
test/whereL.test
Normal file
@ -0,0 +1,39 @@
|
||||
# 2018-07-26
|
||||
#
|
||||
# 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 the WHERE-clause constant propagation
|
||||
# optimization.
|
||||
#
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set ::testprefix whereL
|
||||
|
||||
do_execsql_test 100 {
|
||||
CREATE TABLE t1(a INT PRIMARY KEY, b, c, d, e);
|
||||
CREATE TABLE t2(a INT PRIMARY KEY, f, g, h, i);
|
||||
CREATE TABLE t3(a INT PRIMARY KEY, j, k, l, m);
|
||||
CREATE VIEW v4 AS SELECT * FROM t2 UNION ALL SELECT * FROM t3;
|
||||
}
|
||||
do_eqp_test 110 {
|
||||
SELECT * FROM t1, v4 WHERE t1.a=?1 AND v4.a=t1.a;
|
||||
} {
|
||||
QUERY PLAN
|
||||
|--MATERIALIZE xxxxxx
|
||||
| `--COMPOUND QUERY
|
||||
| |--LEFT-MOST SUBQUERY
|
||||
| | `--SEARCH TABLE t2 USING INDEX sqlite_autoindex_t2_1 (a=?)
|
||||
| `--UNION ALL
|
||||
| `--SEARCH TABLE t3 USING INDEX sqlite_autoindex_t3_1 (a=?)
|
||||
|--SCAN SUBQUERY xxxxxx
|
||||
`--SEARCH TABLE t1 USING INDEX sqlite_autoindex_t1_1 (a=?)
|
||||
}
|
||||
|
||||
finish_test
|
Loading…
x
Reference in New Issue
Block a user