summaryrefslogtreecommitdiff
path: root/src/backend/optimizer/plan
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer/plan')
-rw-r--r--src/backend/optimizer/plan/initsplan.c110
-rw-r--r--src/backend/optimizer/plan/subselect.c168
2 files changed, 103 insertions, 175 deletions
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index 1b3296f971..25117716c3 100644
--- a/src/backend/optimizer/plan/initsplan.c
+++ b/src/backend/optimizer/plan/initsplan.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/plan/initsplan.c,v 1.147 2009/02/20 00:01:03 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/plan/initsplan.c,v 1.148 2009/02/25 03:30:37 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -52,9 +52,6 @@ static void distribute_qual_to_rels(PlannerInfo *root, Node *clause,
Relids qualscope,
Relids ojscope,
Relids outerjoin_nonnullable);
-static void distribute_sublink_quals_to_rels(PlannerInfo *root,
- FlattenedSubLink *fslink,
- bool below_outer_join);
static bool check_outerjoin_delay(PlannerInfo *root, Relids *relids_p,
bool is_pushed_down);
static bool check_redundant_nullability_qual(PlannerInfo *root, Node *clause);
@@ -336,15 +333,9 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
{
Node *qual = (Node *) lfirst(l);
- /* FlattenedSubLink wrappers need special processing */
- if (qual && IsA(qual, FlattenedSubLink))
- distribute_sublink_quals_to_rels(root,
- (FlattenedSubLink *) qual,
- below_outer_join);
- else
- distribute_qual_to_rels(root, qual,
- false, below_outer_join, JOIN_INNER,
- *qualscope, NULL, NULL);
+ distribute_qual_to_rels(root, qual,
+ false, below_outer_join, JOIN_INNER,
+ *qualscope, NULL, NULL);
}
}
else if (IsA(jtnode, JoinExpr))
@@ -399,6 +390,18 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
*inner_join_rels = bms_union(left_inners, right_inners);
nonnullable_rels = leftids;
break;
+ case JOIN_SEMI:
+ leftjoinlist = deconstruct_recurse(root, j->larg,
+ below_outer_join,
+ &leftids, &left_inners);
+ rightjoinlist = deconstruct_recurse(root, j->rarg,
+ below_outer_join,
+ &rightids, &right_inners);
+ *qualscope = bms_union(leftids, rightids);
+ *inner_join_rels = bms_union(left_inners, right_inners);
+ /* Semi join adds no restrictions for quals */
+ nonnullable_rels = NULL;
+ break;
case JOIN_FULL:
leftjoinlist = deconstruct_recurse(root, j->larg,
true,
@@ -425,6 +428,9 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
* semantic scope (ojscope) to pass to distribute_qual_to_rels. But
* we mustn't add it to join_info_list just yet, because we don't want
* distribute_qual_to_rels to think it is an outer join below us.
+ *
+ * Semijoins are a bit of a hybrid: we build a SpecialJoinInfo,
+ * but we want ojscope = NULL for distribute_qual_to_rels.
*/
if (j->jointype != JOIN_INNER)
{
@@ -433,7 +439,11 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
*inner_join_rels,
j->jointype,
(List *) j->quals);
- ojscope = bms_union(sjinfo->min_lefthand, sjinfo->min_righthand);
+ if (j->jointype == JOIN_SEMI)
+ ojscope = NULL;
+ else
+ ojscope = bms_union(sjinfo->min_lefthand,
+ sjinfo->min_righthand);
}
else
{
@@ -446,16 +456,10 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
{
Node *qual = (Node *) lfirst(l);
- /* FlattenedSubLink wrappers need special processing */
- if (qual && IsA(qual, FlattenedSubLink))
- distribute_sublink_quals_to_rels(root,
- (FlattenedSubLink *) qual,
- below_outer_join);
- else
- distribute_qual_to_rels(root, qual,
- false, below_outer_join, j->jointype,
- *qualscope,
- ojscope, nonnullable_rels);
+ distribute_qual_to_rels(root, qual,
+ false, below_outer_join, j->jointype,
+ *qualscope,
+ ojscope, nonnullable_rels);
}
/* Now we can add the SpecialJoinInfo to join_info_list */
@@ -1045,64 +1049,6 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
}
/*
- * distribute_sublink_quals_to_rels
- * Pull sublink quals out of a FlattenedSubLink node and distribute
- * them appropriately; then add a SpecialJoinInfo node to the query's
- * join_info_list. The FlattenedSubLink node itself is no longer
- * needed and does not propagate into further processing.
- */
-static void
-distribute_sublink_quals_to_rels(PlannerInfo *root,
- FlattenedSubLink *fslink,
- bool below_outer_join)
-{
- List *quals = make_ands_implicit(fslink->quals);
- SpecialJoinInfo *sjinfo;
- Relids qualscope;
- Relids ojscope;
- Relids outerjoin_nonnullable;
- ListCell *l;
-
- /*
- * Build a suitable SpecialJoinInfo for the sublink. Note: using
- * righthand as inner_join_rels is the conservative worst case;
- * it might be possible to use a smaller set and thereby allow
- * the sublink join to commute with others inside its RHS.
- */
- sjinfo = make_outerjoininfo(root,
- fslink->lefthand, fslink->righthand,
- fslink->righthand,
- fslink->jointype,
- quals);
-
- /* Treat as inner join if SEMI, outer join if ANTI */
- qualscope = bms_union(sjinfo->syn_lefthand, sjinfo->syn_righthand);
- if (fslink->jointype == JOIN_SEMI)
- {
- ojscope = outerjoin_nonnullable = NULL;
- }
- else
- {
- Assert(fslink->jointype == JOIN_ANTI);
- ojscope = bms_union(sjinfo->min_lefthand, sjinfo->min_righthand);
- outerjoin_nonnullable = fslink->lefthand;
- }
-
- /* Distribute the join quals much as for a regular JOIN node */
- foreach(l, quals)
- {
- Node *qual = (Node *) lfirst(l);
-
- distribute_qual_to_rels(root, qual,
- false, below_outer_join, fslink->jointype,
- qualscope, ojscope, outerjoin_nonnullable);
- }
-
- /* Now we can add the SpecialJoinInfo to join_info_list */
- root->join_info_list = lappend(root->join_info_list, sjinfo);
-}
-
-/*
* check_outerjoin_delay
* Detect whether a qual referencing the given relids must be delayed
* in application due to the presence of a lower outer join, and/or
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index e8484437fd..88dcbc2304 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.145 2009/01/01 17:23:44 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.146 2009/02/25 03:30:37 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -932,12 +932,13 @@ SS_process_ctes(PlannerInfo *root)
}
/*
- * convert_ANY_sublink_to_join: can we convert an ANY SubLink to a join?
+ * convert_ANY_sublink_to_join: try to convert an ANY SubLink to a join
*
* The caller has found an ANY SubLink at the top level of one of the query's
* qual clauses, but has not checked the properties of the SubLink further.
* Decide whether it is appropriate to process this SubLink in join style.
- * Return TRUE if so, FALSE if the SubLink cannot be converted.
+ * If so, form a JoinExpr and return it. Return NULL if the SubLink cannot
+ * be converted to a join.
*
* The only non-obvious input parameter is available_rels: this is the set
* of query rels that can safely be referenced in the sublink expression.
@@ -945,30 +946,33 @@ SS_process_ctes(PlannerInfo *root)
* is present in an outer join's ON qual.) The conversion must fail if
* the converted qual would reference any but these parent-query relids.
*
- * On success, two output parameters are returned:
- * *new_qual is set to the qual tree that should replace the SubLink in
- * the parent query's qual tree. The qual clauses are wrapped in a
- * FlattenedSubLink node to help later processing place them properly.
- * *fromlist is set to a list of pulled-up jointree item(s) that must be
- * added at the proper spot in the parent query's jointree.
+ * On success, the returned JoinExpr has larg = NULL and rarg = the jointree
+ * item representing the pulled-up subquery. The caller must set larg to
+ * represent the relation(s) on the lefthand side of the new join, and insert
+ * the JoinExpr into the upper query's jointree at an appropriate place
+ * (typically, where the lefthand relation(s) had been). Note that the
+ * passed-in SubLink must also be removed from its original position in the
+ * query quals, since the quals of the returned JoinExpr replace it.
+ * (Notionally, we replace the SubLink with a constant TRUE, then elide the
+ * redundant constant from the qual.)
*
* Side effects of a successful conversion include adding the SubLink's
- * subselect to the query's rangetable.
+ * subselect to the query's rangetable, so that it can be referenced in
+ * the JoinExpr's rarg.
*/
-bool
+JoinExpr *
convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink,
- Relids available_rels,
- Node **new_qual, List **fromlist)
+ Relids available_rels)
{
+ JoinExpr *result;
Query *parse = root->parse;
Query *subselect = (Query *) sublink->subselect;
- Relids left_varnos;
+ Relids upper_varnos;
int rtindex;
RangeTblEntry *rte;
RangeTblRef *rtr;
List *subquery_vars;
- Expr *quals;
- FlattenedSubLink *fslink;
+ Node *quals;
Assert(sublink->subLinkType == ANY_SUBLINK);
@@ -977,28 +981,28 @@ convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink,
* higher levels should be okay, though.)
*/
if (contain_vars_of_level((Node *) subselect, 1))
- return false;
+ return NULL;
/*
- * The test expression must contain some Vars of the current query,
+ * The test expression must contain some Vars of the parent query,
* else it's not gonna be a join. (Note that it won't have Vars
* referring to the subquery, rather Params.)
*/
- left_varnos = pull_varnos(sublink->testexpr);
- if (bms_is_empty(left_varnos))
- return false;
+ upper_varnos = pull_varnos(sublink->testexpr);
+ if (bms_is_empty(upper_varnos))
+ return NULL;
/*
* However, it can't refer to anything outside available_rels.
*/
- if (!bms_is_subset(left_varnos, available_rels))
- return false;
+ if (!bms_is_subset(upper_varnos, available_rels))
+ return NULL;
/*
* The combining operators and left-hand expressions mustn't be volatile.
*/
if (contain_volatile_functions(sublink->testexpr))
- return false;
+ return NULL;
/*
* Okay, pull up the sub-select into upper range table.
@@ -1016,13 +1020,10 @@ convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink,
rtindex = list_length(parse->rtable);
/*
- * Form a RangeTblRef for the pulled-up sub-select. This must be added
- * to the upper jointree, but it is caller's responsibility to figure
- * out where.
+ * Form a RangeTblRef for the pulled-up sub-select.
*/
rtr = makeNode(RangeTblRef);
rtr->rtindex = rtindex;
- *fromlist = list_make1(rtr);
/*
* Build a list of Vars representing the subselect outputs.
@@ -1032,53 +1033,45 @@ convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink,
rtindex);
/*
- * Build the replacement qual expression, replacing Params with these Vars.
+ * Build the new join's qual expression, replacing Params with these Vars.
*/
- quals = (Expr *) convert_testexpr(root,
- sublink->testexpr,
- subquery_vars);
+ quals = convert_testexpr(root, sublink->testexpr, subquery_vars);
/*
- * And finally, build the FlattenedSubLink node.
- *
- * Note: at this point left_varnos may well contain join relids, since
- * the testexpr hasn't been run through flatten_join_alias_vars. This
- * will get fixed when flatten_join_alias_vars is run.
+ * And finally, build the JoinExpr node.
*/
- fslink = makeNode(FlattenedSubLink);
- fslink->jointype = JOIN_SEMI;
- fslink->lefthand = left_varnos;
- fslink->righthand = bms_make_singleton(rtindex);
- fslink->quals = quals;
+ result = makeNode(JoinExpr);
+ result->jointype = JOIN_SEMI;
+ result->isNatural = false;
+ result->larg = NULL; /* caller must fill this in */
+ result->rarg = (Node *) rtr;
+ result->using = NIL;
+ result->quals = quals;
+ result->alias = NULL;
+ result->rtindex = 0; /* we don't need an RTE for it */
- *new_qual = (Node *) fslink;
-
- return true;
+ return result;
}
/*
- * convert_EXISTS_sublink_to_join: can we convert an EXISTS SubLink to a join?
+ * convert_EXISTS_sublink_to_join: try to convert an EXISTS SubLink to a join
*
* The API of this function is identical to convert_ANY_sublink_to_join's,
* except that we also support the case where the caller has found NOT EXISTS,
* so we need an additional input parameter "under_not".
*/
-bool
+JoinExpr *
convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink,
- bool under_not,
- Relids available_rels,
- Node **new_qual, List **fromlist)
+ bool under_not, Relids available_rels)
{
+ JoinExpr *result;
Query *parse = root->parse;
Query *subselect = (Query *) sublink->subselect;
Node *whereClause;
int rtoffset;
int varno;
Relids clause_varnos;
- Relids left_varnos;
- Relids right_varnos;
- Relids subselect_varnos;
- FlattenedSubLink *fslink;
+ Relids upper_varnos;
Assert(sublink->subLinkType == EXISTS_SUBLINK);
@@ -1095,13 +1088,13 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink,
* us with noplace to evaluate the targetlist.
*/
if (!simplify_EXISTS_query(subselect))
- return false;
+ return NULL;
/*
* The subquery must have a nonempty jointree, else we won't have a join.
*/
if (subselect->jointree->fromlist == NIL)
- return false;
+ return NULL;
/*
* Separate out the WHERE clause. (We could theoretically also remove
@@ -1116,20 +1109,20 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink,
* query. (Vars of higher levels should be okay, though.)
*/
if (contain_vars_of_level((Node *) subselect, 1))
- return false;
+ return NULL;
/*
* On the other hand, the WHERE clause must contain some Vars of the
* parent query, else it's not gonna be a join.
*/
if (!contain_vars_of_level(whereClause, 1))
- return false;
+ return NULL;
/*
* We don't risk optimizing if the WHERE clause is volatile, either.
*/
if (contain_volatile_functions(whereClause))
- return false;
+ return NULL;
/*
* Prepare to pull up the sub-select into top range table.
@@ -1142,7 +1135,7 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink,
* to do. The machinations of simplify_EXISTS_query ensured that there
* is nothing interesting in the subquery except an rtable and jointree,
* and even the jointree FromExpr no longer has quals. So we can just
- * append the rtable to our own and attach the fromlist to our own.
+ * append the rtable to our own and use the FromExpr in our jointree.
* But first, adjust all level-zero varnos in the subquery to account
* for the rtable merger.
*/
@@ -1161,58 +1154,47 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink,
/*
* Now that the WHERE clause is adjusted to match the parent query
* environment, we can easily identify all the level-zero rels it uses.
- * The ones <= rtoffset are "left rels" of the join we're forming,
- * and the ones > rtoffset are "right rels".
+ * The ones <= rtoffset belong to the upper query; the ones > rtoffset
+ * do not.
*/
clause_varnos = pull_varnos(whereClause);
- left_varnos = right_varnos = NULL;
+ upper_varnos = NULL;
while ((varno = bms_first_member(clause_varnos)) >= 0)
{
if (varno <= rtoffset)
- left_varnos = bms_add_member(left_varnos, varno);
- else
- right_varnos = bms_add_member(right_varnos, varno);
+ upper_varnos = bms_add_member(upper_varnos, varno);
}
bms_free(clause_varnos);
- Assert(!bms_is_empty(left_varnos));
+ Assert(!bms_is_empty(upper_varnos));
/*
* Now that we've got the set of upper-level varnos, we can make the
* last check: only available_rels can be referenced.
*/
- if (!bms_is_subset(left_varnos, available_rels))
- return false;
-
- /* Identify all the rels syntactically within the subselect */
- subselect_varnos = get_relids_in_jointree((Node *) subselect->jointree,
- true);
- Assert(!bms_is_empty(subselect_varnos));
- Assert(bms_is_subset(right_varnos, subselect_varnos));
+ if (!bms_is_subset(upper_varnos, available_rels))
+ return NULL;
/* Now we can attach the modified subquery rtable to the parent */
parse->rtable = list_concat(parse->rtable, subselect->rtable);
/*
- * Pass back the subquery fromlist to be attached to upper jointree
- * in a suitable place.
+ * And finally, build the JoinExpr node.
*/
- *fromlist = subselect->jointree->fromlist;
-
- /*
- * And finally, build the FlattenedSubLink node.
- *
- * Note: at this point left_varnos and subselect_varnos may well contain
- * join relids. This will get fixed when flatten_join_alias_vars is run.
- */
- fslink = makeNode(FlattenedSubLink);
- fslink->jointype = under_not ? JOIN_ANTI : JOIN_SEMI;
- fslink->lefthand = left_varnos;
- fslink->righthand = subselect_varnos;
- fslink->quals = (Expr *) whereClause;
-
- *new_qual = (Node *) fslink;
+ result = makeNode(JoinExpr);
+ result->jointype = under_not ? JOIN_ANTI : JOIN_SEMI;
+ result->isNatural = false;
+ result->larg = NULL; /* caller must fill this in */
+ /* flatten out the FromExpr node if it's useless */
+ if (list_length(subselect->jointree->fromlist) == 1)
+ result->rarg = (Node *) linitial(subselect->jointree->fromlist);
+ else
+ result->rarg = (Node *) subselect->jointree;
+ result->using = NIL;
+ result->quals = whereClause;
+ result->alias = NULL;
+ result->rtindex = 0; /* we don't need an RTE for it */
- return true;
+ return result;
}
/*