summaryrefslogtreecommitdiff
path: root/src/backend/optimizer/path/allpaths.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer/path/allpaths.c')
-rw-r--r--src/backend/optimizer/path/allpaths.c130
1 files changed, 100 insertions, 30 deletions
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 0bf43cab24d..0ee36240ab0 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.99 2003/03/10 03:53:49 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.100 2003/03/22 01:49:38 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -26,7 +26,9 @@
#include "optimizer/plancat.h"
#include "optimizer/planner.h"
#include "optimizer/prep.h"
+#include "optimizer/var.h"
#include "parser/parsetree.h"
+#include "parser/parse_clause.h"
#include "rewrite/rewriteManip.h"
@@ -49,6 +51,7 @@ static RelOptInfo *make_one_rel_by_joins(Query *root, int levels_needed,
List *initial_rels);
static bool subquery_is_pushdown_safe(Query *subquery, Query *topquery);
static bool recurse_pushdown_safe(Node *setOp, Query *topquery);
+static bool qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual);
static void subquery_push_qual(Query *subquery, Index rti, Node *qual);
static void recurse_push_qual(Node *setOp, Query *topquery,
Index rti, Node *qual);
@@ -305,16 +308,14 @@ set_subquery_pathlist(Query *root, RelOptInfo *rel,
*
* There are several cases where we cannot push down clauses.
* Restrictions involving the subquery are checked by
- * subquery_is_pushdown_safe(). Also, 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).
+ * subquery_is_pushdown_safe(). Restrictions on individual clauses are
+ * checked by qual_is_pushdown_safe().
*
* 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?
+ * push down a pushable qual, because it'd result in a worse plan?
*/
if (rel->baserestrictinfo != NIL &&
subquery_is_pushdown_safe(subquery, subquery))
@@ -328,15 +329,15 @@ set_subquery_pathlist(Query *root, RelOptInfo *rel,
RestrictInfo *rinfo = (RestrictInfo *) lfirst(lst);
Node *clause = (Node *) rinfo->clause;
- if (contain_subplans(clause))
+ if (qual_is_pushdown_safe(subquery, rti, clause))
{
- /* Keep it in the upper query */
- upperrestrictlist = lappend(upperrestrictlist, rinfo);
+ /* Push it down */
+ subquery_push_qual(subquery, rti, clause);
}
else
{
- /* Push it down */
- subquery_push_qual(subquery, rti, clause);
+ /* Keep it in the upper query */
+ upperrestrictlist = lappend(upperrestrictlist, rinfo);
}
}
rel->baserestrictinfo = upperrestrictlist;
@@ -527,21 +528,10 @@ make_one_rel_by_joins(Query *root, int levels_needed, List *initial_rels)
*
* Conditions checked here:
*
- * 1. If the subquery has a LIMIT clause or a DISTINCT ON clause, we must
- * not push down any quals, since that could change the set of rows
- * returned. (Actually, we could push down quals into a DISTINCT ON
- * subquery if they refer only to DISTINCT-ed output columns, but
- * checking that seems more work than it's worth. In any case, a
- * plain DISTINCT is safe to push down past.)
- *
- * 2. If the subquery has any functions returning sets in its target list,
- * we do not push down any quals, since the quals
- * might refer to those tlist items, which would mean we'd introduce
- * functions-returning-sets into the subquery's WHERE/HAVING quals.
- * (It'd be sufficient to not push down quals that refer to those
- * particular tlist items, but that's much clumsier to check.)
+ * 1. If the subquery has a LIMIT clause, we must not push down any quals,
+ * since that could change the set of rows returned.
*
- * 3. If the subquery contains EXCEPT or EXCEPT ALL set ops we cannot push
+ * 2. If the subquery contains EXCEPT or EXCEPT ALL set ops we cannot push
* quals into it, because that would change the results. For subqueries
* using UNION/UNION ALL/INTERSECT/INTERSECT ALL, we can push the quals
* into each component query, so long as all the component queries share
@@ -554,11 +544,8 @@ subquery_is_pushdown_safe(Query *subquery, Query *topquery)
{
SetOperationStmt *topop;
- /* Check points 1 and 2 */
- if (subquery->limitOffset != NULL ||
- subquery->limitCount != NULL ||
- has_distinct_on_clause(subquery) ||
- expression_returns_set((Node *) subquery->targetList))
+ /* Check point 1 */
+ if (subquery->limitOffset != NULL || subquery->limitCount != NULL)
return false;
/* Are we at top level, or looking at a setop component? */
@@ -623,6 +610,89 @@ recurse_pushdown_safe(Node *setOp, Query *topquery)
}
/*
+ * qual_is_pushdown_safe - is a particular qual safe to push down?
+ *
+ * qual is a restriction clause applying to the given subquery (whose RTE
+ * has index rti in the parent query).
+ *
+ * Conditions checked here:
+ *
+ * 1. The qual must not contain any subselects (mainly because I'm not sure
+ * it will work correctly: sublinks will already have been transformed into
+ * subplans in the qual, but not in the subquery).
+ *
+ * 2. If the subquery uses DISTINCT ON, we must not push down any quals that
+ * refer to non-DISTINCT output columns, because that could change the set
+ * of rows returned. This condition is vacuous for DISTINCT, because then
+ * there are no non-DISTINCT output columns, but unfortunately it's fairly
+ * expensive to tell the difference between DISTINCT and DISTINCT ON in the
+ * parsetree representation. It's cheaper to just make sure all the Vars
+ * in the qual refer to DISTINCT columns.
+ *
+ * 3. We must not push down any quals that refer to subselect outputs that
+ * return sets, else we'd introduce functions-returning-sets into the
+ * subquery's WHERE/HAVING quals.
+ */
+static bool
+qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual)
+{
+ bool safe = true;
+ List *vars;
+ List *l;
+ Bitmapset *tested = NULL;
+
+ /* Refuse subselects (point 1) */
+ if (contain_subplans(qual))
+ return false;
+
+ /*
+ * Examine all Vars used in clause; since it's a restriction clause,
+ * all such Vars must refer to subselect output columns.
+ */
+ vars = pull_var_clause(qual, false);
+ foreach(l, vars)
+ {
+ Var *var = (Var *) lfirst(l);
+ TargetEntry *tle;
+
+ Assert(var->varno == rti);
+ /*
+ * We use a bitmapset to avoid testing the same attno more than
+ * once. (NB: this only works because subquery outputs can't
+ * have negative attnos.)
+ */
+ if (bms_is_member(var->varattno, tested))
+ continue;
+ tested = bms_add_member(tested, var->varattno);
+
+ tle = (TargetEntry *) nth(var->varattno-1, subquery->targetList);
+ Assert(tle->resdom->resno == var->varattno);
+ Assert(!tle->resdom->resjunk);
+
+ /* If subquery uses DISTINCT or DISTINCT ON, check point 2 */
+ if (subquery->distinctClause != NIL &&
+ !targetIsInSortList(tle, subquery->distinctClause))
+ {
+ /* non-DISTINCT column, so fail */
+ safe = false;
+ break;
+ }
+
+ /* Refuse functions returning sets (point 3) */
+ if (expression_returns_set((Node *) tle->expr))
+ {
+ safe = false;
+ break;
+ }
+ }
+
+ freeList(vars);
+ bms_free(tested);
+
+ return safe;
+}
+
+/*
* subquery_push_qual - push down a qual that we have determined is safe
*/
static void