diff options
Diffstat (limited to 'src/backend/optimizer/path/allpaths.c')
-rw-r--r-- | src/backend/optimizer/path/allpaths.c | 130 |
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 |