*
*
* 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"
#include "optimizer/planner.h"
#include "optimizer/prep.h"
#include "parser/parsetree.h"
+#include "rewrite/rewriteManip.h"
bool enable_geqo = true;
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,
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
List *saved_initplan = PlannerInitPlan;
int saved_planid = PlannerPlanId;
Plan *plan;
+ List *newHaving;
List *lst;
/* Set up for a new level of subquery */
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.
*/
parse->targetList = (List *)
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.
*/
* 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);
}
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,