Split FROM-clause subquery flattening and code generation into separate loops.
FossilOrigin-Name: be8e3fc70e4c13b28b07985df3457960f58ffddd
This commit is contained in:
parent
bc8edba10a
commit
4490c40b90
17
manifest
17
manifest
@ -1,5 +1,5 @@
|
||||
C Provide\sone\sfinal\sSelect\stree\sdump\sprior\sto\sWHERE\sclause\sanalysis\s\nwhen\s".selecttrace\s0x400"\stracing\sbit\sis\sset\swith\sSELECTTRACE_ENABLED.\nAnalysis\sand\sdebug\schanges\sonly\s-\snormal\sbuilds\sare\sunaffected.
|
||||
D 2015-06-05T20:27:26.103
|
||||
C Split\sFROM-clause\ssubquery\sflattening\sand\scode\sgeneration\sinto\sseparate\sloops.
|
||||
D 2015-06-05T22:33:39.408
|
||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||
F Makefile.in 994bab32a3a69e0c35bd148b65cde49879772964
|
||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||
@ -202,7 +202,7 @@ F src/ctime.c 5a0b735dc95604766f5dac73973658eef782ee8b
|
||||
F src/date.c e4d50b3283696836ec1036b695ead9a19e37a5ac
|
||||
F src/dbstat.c f402e77e25089c6003d0c60b3233b9b3947d599a
|
||||
F src/delete.c 37964e6c1d73ff49cbea9ff690c9605fb15f600e
|
||||
F src/expr.c d953b9f03c3c0f701f87a418fcfb9cba8befc6e0
|
||||
F src/expr.c 12e04f322956076b1f8748ca572568968f8155d9
|
||||
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
|
||||
F src/fkey.c c9b63a217d86582c22121699a47f22f524608869
|
||||
F src/func.c 5b8b8e77a0fb644eaf8947d413804622e32692b6
|
||||
@ -250,7 +250,7 @@ F src/printf.c 13ce37e5574f9b0682fa86dbcf9faf76b9d82a15
|
||||
F src/random.c ba2679f80ec82c4190062d756f22d0c358180696
|
||||
F src/resolve.c 84c571794e3ee5806274d95158a4c0177c6c4708
|
||||
F src/rowset.c eccf6af6d620aaa4579bd3b72c1b6395d9e9fa1e
|
||||
F src/select.c b2dfbc9ca9e4d04a34c1b4defdfda7867fee0eb8
|
||||
F src/select.c 6a8f2c442dc69ff343411788e5146b45ddb87609
|
||||
F src/shell.c 07dda7cd692911d2f22269953418d049f2e2c0ee
|
||||
F src/sqlite.h.in d165beeceb6b40af60f352a4d4e37e02d9af7df0
|
||||
F src/sqlite3.rc 992c9f5fb8285ae285d6be28240a7e8d3a7f2bad
|
||||
@ -1282,7 +1282,10 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
|
||||
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
|
||||
F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
|
||||
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
|
||||
P 6a9cf063379118dbb95c6cdc6d60af50e9867177
|
||||
R 7720d4f64678b0c7b4af503d2d9c054f
|
||||
P 283bf0b64da7acc5aa5812fc659954965002d409
|
||||
R 7215f59414fcbc308f2938a5f5f79857
|
||||
T *branch * view-optimization
|
||||
T *sym-view-optimization *
|
||||
T -sym-trunk *
|
||||
U drh
|
||||
Z 95e994f89cb6168fd34736cd7cbf5b60
|
||||
Z b4c3a234d5928895ab25f2763d32a4ad
|
||||
|
@ -1 +1 @@
|
||||
283bf0b64da7acc5aa5812fc659954965002d409
|
||||
be8e3fc70e4c13b28b07985df3457960f58ffddd
|
@ -3559,14 +3559,6 @@ void sqlite3TreeViewExprList(
|
||||
sqlite3TreeViewLine(pView, "%s", zLabel);
|
||||
for(i=0; i<pList->nExpr; i++){
|
||||
sqlite3TreeViewExpr(pView, pList->a[i].pExpr, i<pList->nExpr-1);
|
||||
#if 0
|
||||
if( pList->a[i].zName ){
|
||||
sqlite3ExplainPrintf(pOut, " AS %s", pList->a[i].zName);
|
||||
}
|
||||
if( pList->a[i].bSpanIsTab ){
|
||||
sqlite3ExplainPrintf(pOut, " (%s)", pList->a[i].zSpan);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
sqlite3TreeViewPop(pView);
|
||||
|
188
src/select.c
188
src/select.c
@ -4842,14 +4842,54 @@ int sqlite3Select(
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Generate code for all sub-queries in the FROM clause
|
||||
/* Try to flatten subqueries in the FROM clause into the main query
|
||||
*/
|
||||
#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
|
||||
for(i=0; !p->pPrior && i<pTabList->nSrc; i++){
|
||||
struct SrcList_item *pItem = &pTabList->a[i];
|
||||
SelectDest dest;
|
||||
Select *pSub = pItem->pSelect;
|
||||
int isAggSub;
|
||||
if( pSub==0 ) continue;
|
||||
isAggSub = (pSub->selFlags & SF_Aggregate)!=0;
|
||||
if( flattenSubquery(pParse, p, i, isAgg, isAggSub) ){
|
||||
/* This subquery can be absorbed into its parent. */
|
||||
if( isAggSub ){
|
||||
isAgg = 1;
|
||||
p->selFlags |= SF_Aggregate;
|
||||
}
|
||||
i = -1;
|
||||
}
|
||||
pTabList = p->pSrc;
|
||||
if( db->mallocFailed ) goto select_end;
|
||||
if( !IgnorableOrderby(pDest) ){
|
||||
sSort.pOrderBy = p->pOrderBy;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef SQLITE_OMIT_COMPOUND_SELECT
|
||||
/* Handle compound SELECT statements using the separate multiSelect()
|
||||
** procedure.
|
||||
*/
|
||||
if( p->pPrior ){
|
||||
rc = multiSelect(pParse, p, pDest);
|
||||
explainSetInteger(pParse->iSelectId, iRestoreSelectId);
|
||||
#if SELECTTRACE_ENABLED
|
||||
SELECTTRACE(1,pParse,p,("end compound-select processing\n"));
|
||||
pParse->nSelectIndent--;
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Generate code for all sub-queries in the FROM clause
|
||||
*/
|
||||
#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
|
||||
for(i=0; i<pTabList->nSrc; i++){
|
||||
struct SrcList_item *pItem = &pTabList->a[i];
|
||||
SelectDest dest;
|
||||
Select *pSub = pItem->pSelect;
|
||||
|
||||
if( pSub==0 ) continue;
|
||||
|
||||
@ -4875,87 +4915,73 @@ int sqlite3Select(
|
||||
*/
|
||||
pParse->nHeight += sqlite3SelectExprHeight(p);
|
||||
|
||||
isAggSub = (pSub->selFlags & SF_Aggregate)!=0;
|
||||
if( flattenSubquery(pParse, p, i, isAgg, isAggSub) ){
|
||||
/* This subquery can be absorbed into its parent. */
|
||||
if( isAggSub ){
|
||||
isAgg = 1;
|
||||
p->selFlags |= SF_Aggregate;
|
||||
}
|
||||
i = -1;
|
||||
}else{
|
||||
if( (pItem->jointype & JT_OUTER)==0
|
||||
&& pushDownWhereTerms(db, pSub, p->pWhere, pItem->iCursor)
|
||||
){
|
||||
if( (pItem->jointype & JT_OUTER)==0
|
||||
&& pushDownWhereTerms(db, pSub, p->pWhere, pItem->iCursor)
|
||||
){
|
||||
#if SELECTTRACE_ENABLED
|
||||
if( sqlite3SelectTrace & 0x100 ){
|
||||
SELECTTRACE(0x100,pParse,p,("After WHERE-clause push-down:\n"));
|
||||
sqlite3TreeViewSelect(0, p, 0);
|
||||
}
|
||||
if( sqlite3SelectTrace & 0x100 ){
|
||||
SELECTTRACE(0x100,pParse,p,("After WHERE-clause push-down:\n"));
|
||||
sqlite3TreeViewSelect(0, p, 0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if( pTabList->nSrc==1
|
||||
&& (p->selFlags & SF_All)==0
|
||||
&& OptimizationEnabled(db, SQLITE_SubqCoroutine)
|
||||
){
|
||||
/* Implement a co-routine that will return a single row of the result
|
||||
** set on each invocation.
|
||||
*/
|
||||
int addrTop = sqlite3VdbeCurrentAddr(v)+1;
|
||||
pItem->regReturn = ++pParse->nMem;
|
||||
sqlite3VdbeAddOp3(v, OP_InitCoroutine, pItem->regReturn, 0, addrTop);
|
||||
VdbeComment((v, "%s", pItem->pTab->zName));
|
||||
pItem->addrFillSub = addrTop;
|
||||
sqlite3SelectDestInit(&dest, SRT_Coroutine, pItem->regReturn);
|
||||
explainSetInteger(pItem->iSelectId, (u8)pParse->iNextSelectId);
|
||||
sqlite3Select(pParse, pSub, &dest);
|
||||
pItem->pTab->nRowLogEst = sqlite3LogEst(pSub->nSelectRow);
|
||||
pItem->viaCoroutine = 1;
|
||||
pItem->regResult = dest.iSdst;
|
||||
sqlite3VdbeAddOp1(v, OP_EndCoroutine, pItem->regReturn);
|
||||
sqlite3VdbeJumpHere(v, addrTop-1);
|
||||
sqlite3ClearTempRegCache(pParse);
|
||||
}
|
||||
if( pTabList->nSrc==1
|
||||
&& (p->selFlags & SF_All)==0
|
||||
&& OptimizationEnabled(db, SQLITE_SubqCoroutine)
|
||||
){
|
||||
/* Implement a co-routine that will return a single row of the result
|
||||
** set on each invocation.
|
||||
*/
|
||||
int addrTop = sqlite3VdbeCurrentAddr(v)+1;
|
||||
pItem->regReturn = ++pParse->nMem;
|
||||
sqlite3VdbeAddOp3(v, OP_InitCoroutine, pItem->regReturn, 0, addrTop);
|
||||
VdbeComment((v, "%s", pItem->pTab->zName));
|
||||
pItem->addrFillSub = addrTop;
|
||||
sqlite3SelectDestInit(&dest, SRT_Coroutine, pItem->regReturn);
|
||||
explainSetInteger(pItem->iSelectId, (u8)pParse->iNextSelectId);
|
||||
sqlite3Select(pParse, pSub, &dest);
|
||||
pItem->pTab->nRowLogEst = sqlite3LogEst(pSub->nSelectRow);
|
||||
pItem->viaCoroutine = 1;
|
||||
pItem->regResult = dest.iSdst;
|
||||
sqlite3VdbeAddOp1(v, OP_EndCoroutine, pItem->regReturn);
|
||||
sqlite3VdbeJumpHere(v, addrTop-1);
|
||||
sqlite3ClearTempRegCache(pParse);
|
||||
}else{
|
||||
/* Generate a subroutine that will fill an ephemeral table with
|
||||
** the content of this subquery. pItem->addrFillSub will point
|
||||
** to the address of the generated subroutine. pItem->regReturn
|
||||
** is a register allocated to hold the subroutine return address
|
||||
*/
|
||||
int topAddr;
|
||||
int onceAddr = 0;
|
||||
int retAddr;
|
||||
assert( pItem->addrFillSub==0 );
|
||||
pItem->regReturn = ++pParse->nMem;
|
||||
topAddr = sqlite3VdbeAddOp2(v, OP_Integer, 0, pItem->regReturn);
|
||||
pItem->addrFillSub = topAddr+1;
|
||||
if( pItem->isCorrelated==0 ){
|
||||
/* If the subquery is not correlated and if we are not inside of
|
||||
** a trigger, then we only need to compute the value of the subquery
|
||||
** once. */
|
||||
onceAddr = sqlite3CodeOnce(pParse); VdbeCoverage(v);
|
||||
VdbeComment((v, "materialize \"%s\"", pItem->pTab->zName));
|
||||
}else{
|
||||
/* Generate a subroutine that will fill an ephemeral table with
|
||||
** the content of this subquery. pItem->addrFillSub will point
|
||||
** to the address of the generated subroutine. pItem->regReturn
|
||||
** is a register allocated to hold the subroutine return address
|
||||
*/
|
||||
int topAddr;
|
||||
int onceAddr = 0;
|
||||
int retAddr;
|
||||
assert( pItem->addrFillSub==0 );
|
||||
pItem->regReturn = ++pParse->nMem;
|
||||
topAddr = sqlite3VdbeAddOp2(v, OP_Integer, 0, pItem->regReturn);
|
||||
pItem->addrFillSub = topAddr+1;
|
||||
if( pItem->isCorrelated==0 ){
|
||||
/* If the subquery is not correlated and if we are not inside of
|
||||
** a trigger, then we only need to compute the value of the subquery
|
||||
** once. */
|
||||
onceAddr = sqlite3CodeOnce(pParse); VdbeCoverage(v);
|
||||
VdbeComment((v, "materialize \"%s\"", pItem->pTab->zName));
|
||||
}else{
|
||||
VdbeNoopComment((v, "materialize \"%s\"", pItem->pTab->zName));
|
||||
}
|
||||
sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor);
|
||||
explainSetInteger(pItem->iSelectId, (u8)pParse->iNextSelectId);
|
||||
sqlite3Select(pParse, pSub, &dest);
|
||||
pItem->pTab->nRowLogEst = sqlite3LogEst(pSub->nSelectRow);
|
||||
if( onceAddr ) sqlite3VdbeJumpHere(v, onceAddr);
|
||||
retAddr = sqlite3VdbeAddOp1(v, OP_Return, pItem->regReturn);
|
||||
VdbeComment((v, "end %s", pItem->pTab->zName));
|
||||
sqlite3VdbeChangeP1(v, topAddr, retAddr);
|
||||
sqlite3ClearTempRegCache(pParse);
|
||||
VdbeNoopComment((v, "materialize \"%s\"", pItem->pTab->zName));
|
||||
}
|
||||
sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor);
|
||||
explainSetInteger(pItem->iSelectId, (u8)pParse->iNextSelectId);
|
||||
sqlite3Select(pParse, pSub, &dest);
|
||||
pItem->pTab->nRowLogEst = sqlite3LogEst(pSub->nSelectRow);
|
||||
if( onceAddr ) sqlite3VdbeJumpHere(v, onceAddr);
|
||||
retAddr = sqlite3VdbeAddOp1(v, OP_Return, pItem->regReturn);
|
||||
VdbeComment((v, "end %s", pItem->pTab->zName));
|
||||
sqlite3VdbeChangeP1(v, topAddr, retAddr);
|
||||
sqlite3ClearTempRegCache(pParse);
|
||||
}
|
||||
if( db->mallocFailed ){
|
||||
goto select_end;
|
||||
}
|
||||
pParse->nHeight -= sqlite3SelectExprHeight(p);
|
||||
pTabList = p->pSrc;
|
||||
if( !IgnorableOrderby(pDest) ){
|
||||
sSort.pOrderBy = p->pOrderBy;
|
||||
}
|
||||
}
|
||||
pEList = p->pEList;
|
||||
#endif
|
||||
@ -4971,20 +4997,6 @@ int sqlite3Select(
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef SQLITE_OMIT_COMPOUND_SELECT
|
||||
/* If there is are a sequence of queries, do the earlier ones first.
|
||||
*/
|
||||
if( p->pPrior ){
|
||||
rc = multiSelect(pParse, p, pDest);
|
||||
explainSetInteger(pParse->iSelectId, iRestoreSelectId);
|
||||
#if SELECTTRACE_ENABLED
|
||||
SELECTTRACE(1,pParse,p,("end compound-select processing\n"));
|
||||
pParse->nSelectIndent--;
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* If the query is DISTINCT with an ORDER BY but is not an aggregate, and
|
||||
** if the select-list is the same as the ORDER BY list, then this query
|
||||
** can be rewritten as a GROUP BY. In other words, this:
|
||||
|
Loading…
x
Reference in New Issue
Block a user