From: drh Date: Tue, 2 May 2017 17:54:19 +0000 (+0000) Subject: Reuse the same materialization of a view when that view appears in a query X-Git-Tag: version-3.19.0~27 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=3f4df4c6f3e1fa426617bb6968a455b3f7cfb29d;p=thirdparty%2Fsqlite.git Reuse the same materialization of a view when that view appears in a query more than once, such as in a self-join. FossilOrigin-Name: 9e35c89dbe744312f612e507b51ff9a5bb656def75392d25bc19fc638548cd1e --- 3f4df4c6f3e1fa426617bb6968a455b3f7cfb29d diff --cc manifest index 36affcf425,221fc04ec8..1395c271dd --- a/manifest +++ b/manifest @@@ -1,5 -1,5 +1,5 @@@ - C Move\sterms\sof\sthe\sHAVING\sclause\sthat\sreference\sonly\scolumns\sin\sthe\sGROUP\sBY\nclause\sover\sto\sthe\sWHERE\sclause,\sresulting\sin\sa\sfaster\squery\splan. - D 2017-05-02T16:55:07.827 -C Futher\sbug\sfixes\sto\sthe\sfunction\sthat\sdetermines\swhen\sa\smaterialized\sview\ncan\sbe\sreused. -D 2017-05-01T17:04:35.162 ++C Reuse\sthe\ssame\smaterialization\sof\sa\sview\swhen\sthat\sview\sappears\sin\sa\squery\nmore\sthan\sonce,\ssuch\sas\sin\sa\sself-join. ++D 2017-05-02T17:54:19.760 F Makefile.in 1cc758ce3374a32425e4d130c2fe7b026b20de5b8843243de75f087c0a2661fb F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 6a8c838220f7c00820e1fc0ac1bccaaa8e5676067e1dbfa1bafa7a4ffecf8ae6 @@@ -402,12 -402,12 +402,12 @@@ F src/printf.c 8757834f1b54dae512fb25eb F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384 F src/resolve.c 3e518b962d932a997fae373366880fc028c75706 F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac - F src/select.c 4bbdacd119f22b3b7712b1c1f54bb52fdc7d97d24e131440cc5f235b9df42b0c -F src/select.c 2546672c84240e9b9870d93fdccd7f2de98392fd6836d9bf6611aa60a5ace85f ++F src/select.c 4f0adefaa5e9417459b07757e0f6060cac97930a86f0fba9797bab233ced66c0 F src/shell.c 21b79c0e1b93f8e35fd7b4087d6ba438326c3d7e285d0dd51dfd741475f858a1 -F src/sqlite.h.in 40233103e3e4e10f8a63523498d0259d232e42aba478e2d3fb914799185aced6 +F src/sqlite.h.in eeb1da70a61d52e1d58e5b55446b85bbac571699421d3cf857421c56214013ce F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 58fd0676d3111d02e62e5a35992a7d3da5d3f88753acc174f2d37b774fbbdd28 -F src/sqliteInt.h 9affb53bb405dcea1d86e85198ebaf6232a684cc2b2af6b3c181869f1c8f3e93 +F src/sqliteInt.h aea3aa1b81e0d07d5b1c39b8c5a54a1dc5e4f10136cb63da392aef9eb2a5108b F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b F src/status.c a9e66593dfb28a9e746cba7153f84d49c1ddc4b1 F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34 @@@ -469,7 -469,7 +469,7 @@@ F src/update.c c443935c652af9365e033f75 F src/utf.c 699001c79f28e48e9bcdf8a463da029ea660540c F src/util.c ca8440ede81e155d15cff7c101654f60b55a9ae6 F src/vacuum.c 1fe4555cd8c9b263afb85b5b4ee3a4a4181ad569 - F src/vdbe.c 349eb6789cf8b03ef1aa42271aa9a17a6a7794c07448d27c5405904ba1b21715 -F src/vdbe.c 7c37c36f3d528b10e5a5df76267ca42ed604fb3f15ff1d656afe1f33ff5d1f76 ++F src/vdbe.c 9bac2bc2313ed682e6f48ccff6644d3263341885bfcbb3cdea7b720c722be2d5 F src/vdbe.h f7d1456e28875c2dcb964056589b5b7149ab7edf39edeca801596a39bb3d3848 F src/vdbeInt.h c070bc5c8b913bda0ceaa995cd4d939ded5e4fc96cf7c3c1c602d41b871f8ade F src/vdbeapi.c 5b08d82592bcff4470601fe78aaabebd50837860 @@@ -1578,8 -1577,7 +1578,8 @@@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a9 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 - P 6674814afb9e763e7f7060776586e26da28040b3208ce06c8a285dd647e5a53d 8424492eac506866d2918e5fe03c8f65fef960215d56012a3b52ed42789ed35a - R 645ee5368137c061c8ff554b7c3cb973 - T +closed 8424492eac506866d2918e5fe03c8f65fef960215d56012a3b52ed42789ed35a -P b2aae559581d05e4e8caaddfbd137cb275d582e82d58872c6f42e462dc859b1c -R fb2a58136338dea6025ffd0d391dd6d5 ++P 47cbb471d056c8e1834a5ca72491404a3bfb273b5ff7bdd84b98d263938ea874 c64fe3a1695925693385d313e9ad2a1d8cb37ddaa8cc19920ae0978c91bc4c2c ++R 250cb39f933147d7683c248b1fcd12aa ++T +closed c64fe3a1695925693385d313e9ad2a1d8cb37ddaa8cc19920ae0978c91bc4c2c U drh - Z f125bc7c2e400862ed032d286e9e90a2 -Z 522d6c582c8e206ece3fcf0a3e5fff13 ++Z d1eca8aa444c2ea7562fa3a6e3e0ab31 diff --cc manifest.uuid index 46b6c41181,ac2289d07e..d9ed7e8021 --- a/manifest.uuid +++ b/manifest.uuid @@@ -1,1 -1,1 +1,1 @@@ - 47cbb471d056c8e1834a5ca72491404a3bfb273b5ff7bdd84b98d263938ea874 -c64fe3a1695925693385d313e9ad2a1d8cb37ddaa8cc19920ae0978c91bc4c2c ++9e35c89dbe744312f612e507b51ff9a5bb656def75392d25bc19fc638548cd1e diff --cc src/select.c index 9227be890e,e7a448343f..573a6fa11e --- a/src/select.c +++ b/src/select.c @@@ -4879,77 -4879,32 +4879,103 @@@ static void explainSimpleCount # define explainSimpleCount(a,b,c) #endif +/* +** Context object for havingToWhereExprCb(). +*/ +struct HavingToWhereCtx { + Expr **ppWhere; + ExprList *pGroupBy; +}; + +/* +** sqlite3WalkExpr() callback used by havingToWhere(). +** +** If the node passed to the callback is a TK_AND node, return +** WRC_Continue to tell sqlite3WalkExpr() to iterate through child nodes. +** +** Otherwise, return WRC_Prune. In this case, also check if the +** sub-expression matches the criteria for being moved to the WHERE +** clause. If so, add it to the WHERE clause and replace the sub-expression +** within the HAVING expression with a constant "1". +*/ +static int havingToWhereExprCb(Walker *pWalker, Expr *pExpr){ + if( pExpr->op!=TK_AND ){ + struct HavingToWhereCtx *p = pWalker->u.pHavingCtx; + if( sqlite3ExprIsConstantOrGroupBy(pWalker->pParse, pExpr, p->pGroupBy) ){ + sqlite3 *db = pWalker->pParse->db; + Expr *pNew = sqlite3ExprAlloc(db, TK_INTEGER, &sqlite3IntTokens[1], 0); + if( pNew ){ + Expr *pWhere = *(p->ppWhere); + SWAP(Expr, *pNew, *pExpr); + pNew = sqlite3ExprAnd(db, pWhere, pNew); + *(p->ppWhere) = pNew; + } + } + return WRC_Prune; + } + return WRC_Continue; +} + +/* +** Transfer eligible terms from the HAVING clause of a query, which is +** processed after grouping, to the WHERE clause, which is processed before +** grouping. For example, the query: +** +** SELECT * FROM WHERE a=? GROUP BY b HAVING b=? AND c=? +** +** can be rewritten as: +** +** SELECT * FROM WHERE a=? AND b=? GROUP BY b HAVING c=? +** +** A term of the HAVING expression is eligible for transfer if it consists +** entirely of constants and expressions that are also GROUP BY terms that +** use the "BINARY" collation sequence. +*/ +static void havingToWhere( + Parse *pParse, + ExprList *pGroupBy, + Expr *pHaving, + Expr **ppWhere +){ + struct HavingToWhereCtx sCtx; + Walker sWalker; + + sCtx.ppWhere = ppWhere; + sCtx.pGroupBy = pGroupBy; + + memset(&sWalker, 0, sizeof(sWalker)); + sWalker.pParse = pParse; + sWalker.xExprCallback = havingToWhereExprCb; + sWalker.u.pHavingCtx = &sCtx; + sqlite3WalkExpr(&sWalker, pHaving); +} + + /* + ** Check to see if the pThis entry of pTabList is a self-join of a prior view. + ** If it is, then return the SrcList_item for the prior view. If it is not, + ** then return 0. + */ + static struct SrcList_item *isSelfJoinView( + SrcList *pTabList, /* Search for self-joins in this FROM clause */ + struct SrcList_item *pThis /* Search for prior reference to this subquery */ + ){ + struct SrcList_item *pItem; + for(pItem = pTabList->a; pItempSelect==0 ) continue; + if( pItem->fg.viaCoroutine ) continue; + if( pItem->zName==0 ) continue; + if( sqlite3_stricmp(pItem->zDatabase, pThis->zDatabase)!=0 ) continue; + if( sqlite3_stricmp(pItem->zName, pThis->zName)!=0 ) continue; + if( sqlite3ExprCompare(pThis->pSelect->pWhere, pItem->pSelect->pWhere, -1) ){ + /* The view was modified by some other optimization such as + ** pushDownWhereTerms() */ + continue; + } + return pItem; + } + return 0; + } + /* ** Generate code for the SELECT statement given in the p argument. ** diff --cc src/vdbe.c index 15f015a598,ebf80c8beb..e6c964245f --- a/src/vdbe.c +++ b/src/vdbe.c @@@ -3540,6 -3540,34 +3540,37 @@@ open_cursor_set_hints break; } + /* Opcode: OpenDup P1 P2 * * * + ** + ** Open a new cursor P1 that points to the same ephemeral table as + ** cursor P2. The P2 cursor must have been opened by a prior OP_OpenEphemeral + ** opcode. Only ephemeral cursors may be duplicated. + ** + ** Duplicate ephemeral cursors are used for self-joins of materialized views. + */ + case OP_OpenDup: { + VdbeCursor *pOrig; /* The original cursor to be duplicated */ + VdbeCursor *pCx; /* The new cursor */ + + pOrig = p->apCsr[pOp->p2]; + assert( pOrig->pBtx!=0 ); /* Only ephemeral cursors can be duplicated */ + + pCx = allocateCursor(p, pOp->p1, pOrig->nField, -1, CURTYPE_BTREE); + if( pCx==0 ) goto no_mem; + pCx->nullRow = 1; + pCx->isEphemeral = 1; + pCx->pKeyInfo = pOrig->pKeyInfo; + pCx->isTable = pOrig->isTable; + rc = sqlite3BtreeCursor(pOrig->pBtx, MASTER_ROOT, BTREE_WRCSR, + pCx->pKeyInfo, pCx->uc.pCursor); - if( rc ) goto abort_due_to_error; ++ /* The sqlite3BtreeCursor() routine can only fail for the first cursor ++ ** opened for a database. Since there is already an open cursor when this ++ ** opcode is run, the sqlite3BtreeCursor() cannot fail */ ++ assert( rc==SQLITE_OK ); + break; + } + + /* Opcode: OpenEphemeral P1 P2 * P4 P5 ** Synopsis: nColumn=P2 **