]> granicus.if.org Git - postgresql/commitdiff
Fix performance issue with qualifications on VIEWs: outer query should
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 18 Jan 2001 07:12:37 +0000 (07:12 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 18 Jan 2001 07:12:37 +0000 (07:12 +0000)
try to push restrictions on the view down into the view subquery,
so that they can become indexscan quals or what-have-you rather than
being applied at the top level of the subquery.  7.0 and before were
able to do this, though in a much klugier way, and I'd hate to have
anyone complaining that 7.1 is stupider than 7.0 ...

src/backend/optimizer/path/allpaths.c
src/backend/optimizer/plan/planner.c

index 4f7a0e570f5581f549267b5aacf0893f29cd8e76..5e461cb3c15e2748347e5327e48c56984ebad1f5 100644 (file)
@@ -8,13 +8,14 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.68 2000/12/14 22:30:43 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.69 2001/01/18 07:12:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 #include "postgres.h"
 
+#include "optimizer/clauses.h"
 #include "optimizer/cost.h"
 #include "optimizer/geqo.h"
 #include "optimizer/pathnode.h"
@@ -23,6 +24,7 @@
 #include "optimizer/planner.h"
 #include "optimizer/prep.h"
 #include "parser/parsetree.h"
+#include "rewrite/rewriteManip.h"
 
 
 bool           enable_geqo = true;
@@ -99,12 +101,65 @@ set_base_rel_pathlists(Query *root)
                if (rel->issubquery)
                {
                        /* Subquery --- generate a separate plan for it */
+                       List       *upperrestrictlist;
+                       List       *lst;
 
                        /*
-                        * XXX for now, we just apply any restrict clauses that came
-                        * from the outer query as qpquals of the SubqueryScan node.
-                        * Later, think about pushing them down into the subquery itself.
+                        * If there are any restriction clauses that have been attached
+                        * to the subquery relation, consider pushing them down to become
+                        * HAVING quals of the subquery itself.  (Not WHERE clauses, since
+                        * they may refer to subquery outputs that are aggregate results.
+                        * But planner.c will transfer them into the subquery's WHERE if
+                        * they do not.)  This transformation is useful because it may
+                        * allow us to generate a better plan for the subquery than
+                        * evaluating all the subquery output rows and then filtering
+                        * them.
+                        *
+                        * Currently, we do not push down clauses that contain subselects,
+                        * mainly because I'm not sure it will work correctly (the
+                        * subplan hasn't yet transformed sublinks to subselects).
+                        * Non-pushed-down clauses will get evaluated as qpquals of
+                        * the SubqueryScan node.
+                        *
+                        * XXX Are there any cases where we want to make a policy
+                        * decision not to push down, because it'd result in a worse
+                        * plan?
                         */
+                       upperrestrictlist = NIL;
+                       foreach(lst, rel->baserestrictinfo)
+                       {
+                               RestrictInfo   *rinfo = (RestrictInfo *) lfirst(lst);
+                               Node               *clause = (Node *) rinfo->clause;
+
+                               if (contain_subplans(clause))
+                               {
+                                       /* Keep it in the upper query */
+                                       upperrestrictlist = lappend(upperrestrictlist, rinfo);
+                               }
+                               else
+                               {
+                                       /*
+                                        * We need to replace Vars in the clause (which must
+                                        * refer to outputs of the subquery) with copies of the
+                                        * subquery's targetlist expressions.  Note that at this
+                                        * point, any uplevel Vars in the clause should have been
+                                        * replaced with Params, so they need no work.
+                                        */
+                                       clause = ResolveNew(clause, rti, 0,
+                                                                               rte->subquery->targetList,
+                                                                               CMD_SELECT, 0);
+                                       rte->subquery->havingQual =
+                                               make_and_qual(rte->subquery->havingQual,
+                                                                         clause);
+                                       /*
+                                        * We need not change the subquery's hasAggs or
+                                        * hasSublinks flags, since we can't be pushing down
+                                        * any aggregates that weren't there before, and we
+                                        * don't push down subselects at all.
+                                        */
+                               }
+                       }
+                       rel->baserestrictinfo = upperrestrictlist;
 
                        /* Generate the plan for the subquery */
                        rel->subplan = subquery_planner(rte->subquery,
index 5d845e9842a6a44a23df384ac04304753dedfa64..d2ec1b8a750ff6d0618d226cab9471e207381ff8 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.98 2000/12/14 22:30:43 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.99 2001/01/18 07:12:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -132,6 +132,7 @@ subquery_planner(Query *parse, double tuple_fraction)
        List       *saved_initplan = PlannerInitPlan;
        int                     saved_planid = PlannerPlanId;
        Plan       *plan;
+       List       *newHaving;
        List       *lst;
 
        /* Set up for a new level of subquery */
@@ -155,20 +156,6 @@ subquery_planner(Query *parse, double tuple_fraction)
        parse->jointree = (FromExpr *)
                preprocess_jointree(parse, (Node *) parse->jointree);
 
-       /*
-        * A HAVING clause without aggregates is equivalent to a WHERE clause
-        * (except it can only refer to grouped fields).  If there are no aggs
-        * anywhere in the query, then we don't want to create an Agg plan
-        * node, so merge the HAVING condition into WHERE.      (We used to
-        * consider this an error condition, but it seems to be legal SQL.)
-        */
-       if (parse->havingQual != NULL && !parse->hasAggs)
-       {
-               parse->jointree->quals = make_and_qual(parse->jointree->quals,
-                                                                                          parse->havingQual);
-               parse->havingQual = NULL;
-       }
-
        /*
         * Do expression preprocessing on targetlist and quals.
         */
@@ -181,6 +168,37 @@ subquery_planner(Query *parse, double tuple_fraction)
        parse->havingQual = preprocess_expression(parse, parse->havingQual,
                                                                                          EXPRKIND_HAVING);
 
+       /*
+        * A HAVING clause without aggregates is equivalent to a WHERE clause
+        * (except it can only refer to grouped fields).  Transfer any agg-free
+        * clauses of the HAVING qual into WHERE.  This may seem like wasting
+        * cycles to cater to stupidly-written queries, but there are other
+        * reasons for doing it.  Firstly, if the query contains no aggs at all,
+        * then we aren't going to generate an Agg plan node, and so there'll be
+        * no place to execute HAVING conditions; without this transfer, we'd
+        * lose the HAVING condition entirely, which is wrong.  Secondly, when
+        * we push down a qual condition into a sub-query, it's easiest to push
+        * the qual into HAVING always, in case it contains aggs, and then let
+        * this code sort it out.
+        *
+        * Note that both havingQual and parse->jointree->quals are in
+        * implicitly-ANDed-list form at this point, even though they are
+        * declared as Node *.  Also note that contain_agg_clause does not
+        * recurse into sub-selects, which is exactly what we need here.
+        */
+       newHaving = NIL;
+       foreach(lst, (List *) parse->havingQual)
+       {
+               Node   *havingclause = (Node *) lfirst(lst);
+
+               if (contain_agg_clause(havingclause))
+                       newHaving = lappend(newHaving, havingclause);
+               else
+                       parse->jointree->quals = (Node *)
+                               lappend((List *) parse->jointree->quals, havingclause);
+       }
+       parse->havingQual = (Node *) newHaving;
+
        /*
         * Do the main planning.  If we have an inherited target relation,
         * that needs special processing, else go straight to grouping_planner.
@@ -554,12 +572,6 @@ preprocess_expression(Query *parse, Node *expr, int kind)
                         * Check for ungrouped variables passed to subplans.  Note we
                         * do NOT do this for subplans in WHERE (or JOIN/ON); it's legal
                         * there because WHERE is evaluated pre-GROUP.
-                        *
-                        * An interesting fine point: if subquery_planner reassigned a
-                        * HAVING qual into WHERE, then we will accept references to
-                        * ungrouped vars from subplans in the HAVING qual.  This is not
-                        * entirely consistent, but it doesn't seem particularly
-                        * harmful...
                         */
                        check_subplans_for_ungrouped_vars(expr, parse);
                }
@@ -1049,6 +1061,11 @@ grouping_planner(Query *parse, double tuple_fraction)
                                                                                result_plan);
                /* Note: Agg does not affect any existing sort order of the tuples */
        }
+       else
+       {
+               /* If there are no Aggs, we shouldn't have any HAVING qual anymore */
+               Assert(parse->havingQual == NULL);
+       }
 
        /*
         * If we were not able to make the plan come out in the right order,