Attempt to omit ORDER BY clauses from FROM-clause subqueries if those ORDER BY

clauses do not affect the output.  See
[forum:/forumpost/2d76f2bcf65d256a|forum thread 2d76f2bcf65d256a] for
discussion.  This can help the query flattener in
some cases, resulting in faster query plans.  The current implemention does
not always work.

FossilOrigin-Name: ef97c3e7c3ea2cf1a4db6591328fe7ce3f1d189afc2d578159135824ec89e620
This commit is contained in:
drh 2021-07-15 19:29:43 +00:00
parent 480f5e3e6e
commit bb30123178
7 changed files with 98 additions and 57 deletions

View File

@ -1,5 +1,5 @@
C Change\sreferences\sto\sthe\sschema\stable\sin\ssqlite3_analyzer\sto\sthe\snew\nSQLITE_SCHEMA\sname.
D 2021-07-15T16:39:42.407
C Attempt\sto\somit\sORDER\sBY\sclauses\sfrom\sFROM-clause\ssubqueries\sif\sthose\sORDER\sBY\nclauses\sdo\snot\saffect\sthe\soutput.\s\sSee\n[forum:/forumpost/2d76f2bcf65d256a|forum\sthread\s2d76f2bcf65d256a]\sfor\ndiscussion.\s\sThis\scan\shelp\sthe\squery\sflattener\sin\nsome\scases,\sresulting\sin\sfaster\squery\splans.\s\sThe\scurrent\simplemention\sdoes\nnot\salways\swork.
D 2021-07-15T19:29:43.004
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@ -499,7 +499,7 @@ F src/delete.c 62451bba9fe641159e9c0b7d9d2bab1c48d0cff11e16de2d14000603d2af1fcf
F src/expr.c 62b6d0ac8ee30a4749b78b7b75ae951a911a5d49321af5fe41c05af4df9e7537
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
F src/fkey.c e9063648396c58778f77583a678342fe4a9bc82436bf23c5f9f444f2df0fdaa4
F src/func.c c96ac6f7c4f2d684217c4673a80446e1b50e25b5ea79366f333f484622d010a0
F src/func.c c224240cbc97fa5e9c4fe9acb128716cb835ca045532bca6951b7c45b020c56c
F src/global.c 25ba4d58476f6be29bba9d9d14f7f146b78476d3a4d75ebb8c3b736328afe0f9
F src/hash.c 8d7dda241d0ebdafb6ffdeda3149a412d7df75102cecfc1021c98d6219823b19
F src/hash.h 9d56a9079d523b648774c1784b74b89bd93fac7b365210157482e4319a468f38
@ -542,14 +542,14 @@ F src/pragma.h 8dc78ab7e9ec6ce3ded8332810a2066f1ef6267e2e03cd7356ee00276125c6cf
F src/prepare.c 0d53d20532aada295c1690792a125adbd6435f5ce703ff0adf1b9b3605238b67
F src/printf.c 78fabb49b9ac9a12dd1c89d744abdc9b67fd3205e62967e158f78b965a29ec4b
F src/random.c 097dc8b31b8fba5a9aca1697aeb9fd82078ec91be734c16bffda620ced7ab83c
F src/resolve.c b379c5ffe3b692e9c64fa37817cc0efa204b7c9468a818309dde85fd132d9d81
F src/resolve.c 9b54a60702c35b3efbeb5d0439a94d67c4cb93a896e1afa89f21d7a9d183a071
F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
F src/select.c 1c1230aaa4e5979edd3c5a9f7a90ab856a302cca4962e8a7d08f42dd2e7e1a49
F src/select.c cc61c6b86617387803d67994fec6131365f6a049399aa28b11028ad0b4ee53b8
F src/shell.c.in 856de2945bb7fdfdeebe7136cf1b59d24618845aa5e5f3937fda7ff37c623b51
F src/sqlite.h.in a5d5641afa335226231231c7b6e424de327f17577d9e8aeaaaab78d9bbbcf381
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h e97f4e9b509408fea4c4e9bef5a41608dfac343b4d3c7a990dedde1e19af9510
F src/sqliteInt.h 129619232aa632fa85db7792bfe4b378ed28d3361bf9cbcdb4522353fd04b89b
F src/sqliteInt.h 74ca9667448fa7aaadcb91bd3fd22732c7f7e05903a53429e653a4a01e4c8efd
F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657
F src/status.c 4b8bc2a6905163a38b739854a35b826c737333fab5b1f8e03fa7eb9a4799c4c1
F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
@ -635,7 +635,7 @@ F src/where.c 07a4097fe42a01b1f99d2a136598654051f0bdcd6c17cbef7fa285a9cf21e4d2
F src/whereInt.h 9248161dd004f625ce5d3841ca9b99fed3fc8d61522cf76340fc5217dbe1375b
F src/wherecode.c 9f1f65d11437b25cd0a1497a170514c785f19ce6ad9d3e6fc73719cb5a49012f
F src/whereexpr.c 2bfb1cd24b9b63fc5e32b9b85f2b3f88765bdac2aab51102d94d1fb56ec2917b
F src/window.c 559961a731f67a0873744960bdeeb034a5c74cc0a09badb14bc43f74744a2a9d
F src/window.c 420167512050a0dfc0f0115b9f0c7d299da9759c9bb2ae83a61fb8d730a5707f
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
F test/affinity2.test ce1aafc86e110685b324e9a763eab4f2a73f737842ec3b687bd965867de90627
F test/affinity3.test eecb0dabee4b7765a8465439d5e99429279ffba23ca74a7eae270a452799f9e7
@ -1920,7 +1920,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 1594056aab068b9fae82f6b885828c7127d9bedcc37c4340486e32791bc87c7a
R 559c73747c40f0a1a7e76968f3947fcc
P 3beb77d6a792d07ca605ec8e5402b8a305860cba4044fcafeed00b9fa862fa16
R be1da55dd559418ff2dfd4ecbe7a006e
T *branch * omit-subquery-order-by
T *sym-omit-subquery-order-by *
T -sym-trunk *
U drh
Z 82febd786cf0b2a285b7ed5af3f23080
Z fa9c090cb9dd3863cd70130aca6f84ef

View File

@ -1 +1 @@
3beb77d6a792d07ca605ec8e5402b8a305860cba4044fcafeed00b9fa862fa16
ef97c3e7c3ea2cf1a4db6591328fe7ce3f1d189afc2d578159135824ec89e620

View File

@ -2160,11 +2160,11 @@ void sqlite3RegisterBuiltinFunctions(void){
FUNCTION(min, -1, 0, 1, minmaxFunc ),
FUNCTION(min, 0, 0, 1, 0 ),
WAGGREGATE(min, 1, 0, 1, minmaxStep, minMaxFinalize, minMaxValue, 0,
SQLITE_FUNC_MINMAX ),
SQLITE_FUNC_MINMAX|SQLITE_FUNC_ANYORDER ),
FUNCTION(max, -1, 1, 1, minmaxFunc ),
FUNCTION(max, 0, 1, 1, 0 ),
WAGGREGATE(max, 1, 1, 1, minmaxStep, minMaxFinalize, minMaxValue, 0,
SQLITE_FUNC_MINMAX ),
SQLITE_FUNC_MINMAX|SQLITE_FUNC_ANYORDER ),
FUNCTION2(typeof, 1, 0, 0, typeofFunc, SQLITE_FUNC_TYPEOF),
FUNCTION2(length, 1, 0, 0, lengthFunc, SQLITE_FUNC_LENGTH),
FUNCTION(instr, 2, 0, 0, instrFunc ),
@ -2200,9 +2200,10 @@ void sqlite3RegisterBuiltinFunctions(void){
WAGGREGATE(total, 1,0,0, sumStep,totalFinalize,totalFinalize,sumInverse, 0),
WAGGREGATE(avg, 1,0,0, sumStep, avgFinalize, avgFinalize, sumInverse, 0),
WAGGREGATE(count, 0,0,0, countStep,
countFinalize, countFinalize, countInverse, SQLITE_FUNC_COUNT ),
countFinalize, countFinalize, countInverse,
SQLITE_FUNC_COUNT|SQLITE_FUNC_ANYORDER ),
WAGGREGATE(count, 1,0,0, countStep,
countFinalize, countFinalize, countInverse, 0 ),
countFinalize, countFinalize, countInverse, SQLITE_FUNC_ANYORDER ),
WAGGREGATE(group_concat, 1, 0, 0, groupConcatStep,
groupConcatFinalize, groupConcatValue, groupConcatInverse, 0),
WAGGREGATE(group_concat, 2, 0, 0, groupConcatStep,

View File

@ -1087,9 +1087,12 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
assert( pDef!=0 || IN_RENAME_OBJECT );
if( pNC2 && pDef ){
assert( SQLITE_FUNC_MINMAX==NC_MinMaxAgg );
assert( SQLITE_FUNC_ANYORDER==NC_OrderAgg );
testcase( (pDef->funcFlags & SQLITE_FUNC_MINMAX)!=0 );
pNC2->ncFlags |= NC_HasAgg | (pDef->funcFlags & SQLITE_FUNC_MINMAX);
testcase( (pDef->funcFlags & SQLITE_FUNC_ANYORDER)!=0 );
pNC2->ncFlags |= NC_HasAgg
| ((pDef->funcFlags^SQLITE_FUNC_ANYORDER)
& (SQLITE_FUNC_MINMAX|SQLITE_FUNC_ANYORDER));
}
}
pNC->ncFlags |= savedAllowFlags;
@ -1676,7 +1679,8 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
pGroupBy = p->pGroupBy;
if( pGroupBy || (sNC.ncFlags & NC_HasAgg)!=0 ){
assert( NC_MinMaxAgg==SF_MinMaxAgg );
p->selFlags |= SF_Aggregate | (sNC.ncFlags&NC_MinMaxAgg);
assert( NC_OrderAgg==SF_OrderByReqd );
p->selFlags |= SF_Aggregate | (sNC.ncFlags&(NC_MinMaxAgg|NC_OrderAgg));
}else{
sNC.ncFlags &= ~NC_AllowAgg;
}
@ -1859,8 +1863,8 @@ int sqlite3ResolveExprNames(
Walker w;
if( pExpr==0 ) return SQLITE_OK;
savedHasAgg = pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin);
pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg|NC_HasWin);
savedHasAgg = pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin|NC_OrderAgg);
pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg|NC_HasWin|NC_OrderAgg);
w.pParse = pNC->pParse;
w.xExprCallback = resolveExprStep;
w.xSelectCallback = (pNC->ncFlags & NC_NoSelect) ? 0 : resolveSelectStep;
@ -1903,8 +1907,8 @@ int sqlite3ResolveExprListNames(
w.xSelectCallback = resolveSelectStep;
w.xSelectCallback2 = 0;
w.u.pNC = pNC;
savedHasAgg = pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin);
pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg|NC_HasWin);
savedHasAgg = pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin|NC_OrderAgg);
pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg|NC_HasWin|NC_OrderAgg);
for(i=0; i<pList->nExpr; i++){
Expr *pExpr = pList->a[i].pExpr;
if( pExpr==0 ) continue;
@ -1922,10 +1926,11 @@ int sqlite3ResolveExprListNames(
assert( EP_Win==NC_HasWin );
testcase( pNC->ncFlags & NC_HasAgg );
testcase( pNC->ncFlags & NC_HasWin );
if( pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin) ){
if( pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin|NC_OrderAgg) ){
ExprSetProperty(pExpr, pNC->ncFlags & (NC_HasAgg|NC_HasWin) );
savedHasAgg |= pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin);
pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg|NC_HasWin);
savedHasAgg |= pNC->ncFlags &
(NC_HasAgg|NC_MinMaxAgg|NC_HasWin|NC_OrderAgg);
pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg|NC_HasWin|NC_OrderAgg);
}
if( w.pParse->nErr>0 ) return WRC_Abort;
}

View File

@ -6384,6 +6384,31 @@ int sqlite3Select(
if( (pSub->selFlags & SF_Aggregate)!=0 ) continue;
assert( pSub->pGroupBy==0 );
/* If a FROM-clause subquery has an ORDER BY clause that is not
** really doing anything, then delete it now so that it does not
** interfere with query flattening.
**
** Beware of these cases where the ORDER BY clause may not be safely
** omitted:
**
** (1) There is also a LIMIT clause
** (2) The subquery was added to help with window-function
** processing
** (3) The outer query uses an aggregate function other than
** the built-in count(), min(), or max().
*/
if( pSub->pOrderBy!=0
&& pSub->pLimit==0 /* Condition (1) */
&& (pSub->selFlags & SF_OrderByReqd)==0 /* Condition (2) */
&& (p->selFlags & SF_OrderByReqd)==0 /* Condition (3) */
&& OptimizationEnabled(db, SQLITE_OmitOrderBy)
){
SELECTTRACE(0x100,pParse,p,
("omit superfluous ORDER BY on %r FROM-clause subquery\n",i+1));
sqlite3ExprListDelete(db, pSub->pOrderBy);
pSub->pOrderBy = 0;
}
/* If the outer query contains a "complex" result set (that is,
** if the result set of the outer query uses functions or subqueries)
** and if the subquery contains an ORDER BY clause and if

View File

@ -1744,6 +1744,7 @@ struct sqlite3 {
#define SQLITE_PropagateConst 0x00008000 /* The constant propagation opt */
#define SQLITE_MinMaxOpt 0x00010000 /* The min/max optimization */
#define SQLITE_SeekScan 0x00020000 /* The OP_SeekScan optimization */
#define SQLITE_OmitOrderBy 0x00040000 /* Omit pointless ORDER BY */
#define SQLITE_AllOpts 0xffffffff /* All optimizations */
/*
@ -1823,12 +1824,13 @@ struct FuncDestructor {
** are assert() statements in the code to verify this.
**
** Value constraints (enforced via assert()):
** SQLITE_FUNC_MINMAX == NC_MinMaxAgg == SF_MinMaxAgg
** SQLITE_FUNC_LENGTH == OPFLAG_LENGTHARG
** SQLITE_FUNC_TYPEOF == OPFLAG_TYPEOFARG
** SQLITE_FUNC_CONSTANT == SQLITE_DETERMINISTIC from the API
** SQLITE_FUNC_DIRECT == SQLITE_DIRECTONLY from the API
** SQLITE_FUNC_UNSAFE == SQLITE_INNOCUOUS
** SQLITE_FUNC_MINMAX == NC_MinMaxAgg == SF_MinMaxAgg
** SQLITE_FUNC_ANYORDER == NC_OrderAgg == SF_OrderByReqd
** SQLITE_FUNC_LENGTH == OPFLAG_LENGTHARG
** SQLITE_FUNC_TYPEOF == OPFLAG_TYPEOFARG
** SQLITE_FUNC_CONSTANT == SQLITE_DETERMINISTIC from the API
** SQLITE_FUNC_DIRECT == SQLITE_DIRECTONLY from the API
** SQLITE_FUNC_UNSAFE == SQLITE_INNOCUOUS
** SQLITE_FUNC_ENCMASK depends on SQLITE_UTF* macros in the API
*/
#define SQLITE_FUNC_ENCMASK 0x0003 /* SQLITE_UTF8, SQLITE_UTF16BE or UTF16LE */
@ -1853,6 +1855,7 @@ struct FuncDestructor {
#define SQLITE_FUNC_SUBTYPE 0x00100000 /* Result likely to have sub-type */
#define SQLITE_FUNC_UNSAFE 0x00200000 /* Function has side effects */
#define SQLITE_FUNC_INLINE 0x00400000 /* Functions implemented in-line */
#define SQLITE_FUNC_ANYORDER 0x08000000 /* count/min/max aggregate */
/* Identifier numbers for each in-line function */
#define INLINEFUNC_coalesce 0
@ -3088,31 +3091,33 @@ struct NameContext {
** Allowed values for the NameContext, ncFlags field.
**
** Value constraints (all checked via assert()):
** NC_HasAgg == SF_HasAgg == EP_Agg
** NC_MinMaxAgg == SF_MinMaxAgg == SQLITE_FUNC_MINMAX
** NC_HasAgg == SF_HasAgg == EP_Agg
** NC_MinMaxAgg == SF_MinMaxAgg == SQLITE_FUNC_MINMAX
** NC_OrderAgg == SF_OrderByReqd == SQLITE_FUNC_ANYORDER
** NC_HasWin == EP_Win
**
*/
#define NC_AllowAgg 0x00001 /* Aggregate functions are allowed here */
#define NC_PartIdx 0x00002 /* True if resolving a partial index WHERE */
#define NC_IsCheck 0x00004 /* True if resolving a CHECK constraint */
#define NC_GenCol 0x00008 /* True for a GENERATED ALWAYS AS clause */
#define NC_HasAgg 0x00010 /* One or more aggregate functions seen */
#define NC_IdxExpr 0x00020 /* True if resolving columns of CREATE INDEX */
#define NC_SelfRef 0x0002e /* Combo: PartIdx, isCheck, GenCol, and IdxExpr */
#define NC_VarSelect 0x00040 /* A correlated subquery has been seen */
#define NC_UEList 0x00080 /* True if uNC.pEList is used */
#define NC_UAggInfo 0x00100 /* True if uNC.pAggInfo is used */
#define NC_UUpsert 0x00200 /* True if uNC.pUpsert is used */
#define NC_UBaseReg 0x00400 /* True if uNC.iBaseReg is used */
#define NC_MinMaxAgg 0x01000 /* min/max aggregates seen. See note above */
#define NC_Complex 0x02000 /* True if a function or subquery seen */
#define NC_AllowWin 0x04000 /* Window functions are allowed here */
#define NC_HasWin 0x08000 /* One or more window functions seen */
#define NC_IsDDL 0x10000 /* Resolving names in a CREATE statement */
#define NC_InAggFunc 0x20000 /* True if analyzing arguments to an agg func */
#define NC_FromDDL 0x40000 /* SQL text comes from sqlite_schema */
#define NC_NoSelect 0x80000 /* Do not descend into sub-selects */
#define NC_AllowAgg 0x000001 /* Aggregate functions are allowed here */
#define NC_PartIdx 0x000002 /* True if resolving a partial index WHERE */
#define NC_IsCheck 0x000004 /* True if resolving a CHECK constraint */
#define NC_GenCol 0x000008 /* True for a GENERATED ALWAYS AS clause */
#define NC_HasAgg 0x000010 /* One or more aggregate functions seen */
#define NC_IdxExpr 0x000020 /* True if resolving columns of CREATE INDEX */
#define NC_SelfRef 0x00002e /* Combo: PartIdx, isCheck, GenCol, and IdxExpr */
#define NC_VarSelect 0x000040 /* A correlated subquery has been seen */
#define NC_UEList 0x000080 /* True if uNC.pEList is used */
#define NC_UAggInfo 0x000100 /* True if uNC.pAggInfo is used */
#define NC_UUpsert 0x000200 /* True if uNC.pUpsert is used */
#define NC_UBaseReg 0x000400 /* True if uNC.iBaseReg is used */
#define NC_MinMaxAgg 0x001000 /* min/max aggregates seen. See note above */
#define NC_Complex 0x002000 /* True if a function or subquery seen */
#define NC_AllowWin 0x004000 /* Window functions are allowed here */
#define NC_HasWin 0x008000 /* One or more window functions seen */
#define NC_IsDDL 0x010000 /* Resolving names in a CREATE statement */
#define NC_InAggFunc 0x020000 /* True if analyzing arguments to an agg func */
#define NC_FromDDL 0x040000 /* SQL text comes from sqlite_schema */
#define NC_NoSelect 0x080000 /* Do not descend into sub-selects */
#define NC_OrderAgg 0x8000000 /* Has an aggregate other than count/min/max */
/*
** An instance of the following object describes a single ON CONFLICT
@ -3195,9 +3200,10 @@ struct Select {
** "Select Flag".
**
** Value constraints (all checked via assert())
** SF_HasAgg == NC_HasAgg
** SF_MinMaxAgg == NC_MinMaxAgg == SQLITE_FUNC_MINMAX
** SF_FixedLimit == WHERE_USE_LIMIT
** SF_HasAgg == NC_HasAgg
** SF_MinMaxAgg == NC_MinMaxAgg == SQLITE_FUNC_MINMAX
** SF_OrderByReqd == NC_OrderAgg == SQLITE_FUNC_ANYORDER
** SF_FixedLimit == WHERE_USE_LIMIT
*/
#define SF_Distinct 0x0000001 /* Output should be DISTINCT */
#define SF_All 0x0000002 /* Includes the ALL keyword */
@ -3226,6 +3232,7 @@ struct Select {
#define SF_PushDown 0x1000000 /* SELECT has be modified by push-down opt */
#define SF_MultiPart 0x2000000 /* Has multiple incompatible PARTITIONs */
#define SF_CopyCte 0x4000000 /* SELECT statement is a copy of a CTE */
#define SF_OrderByReqd 0x8000000 /* The ORDER BY clause may not be purged */
/*
** The results of a SELECT can be distributed in several ways, as defined

View File

@ -1070,7 +1070,7 @@ int sqlite3WindowRewrite(Parse *pParse, Select *p){
Table *pTab2;
p->pSrc->a[0].pSelect = pSub;
sqlite3SrcListAssignCursors(pParse, p->pSrc);
pSub->selFlags |= SF_Expanded;
pSub->selFlags |= SF_Expanded|SF_OrderByReqd;
pTab2 = sqlite3ResultSetOfSelect(pParse, pSub, SQLITE_AFF_NONE);
pSub->selFlags |= (selFlags & SF_Aggregate);
if( pTab2==0 ){