aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer/plan/subselect.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer/plan/subselect.c')
-rw-r--r--src/backend/optimizer/plan/subselect.c126
1 files changed, 74 insertions, 52 deletions
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index 1e4e9fe565b..d4374516ac8 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.133 2008/08/14 18:47:59 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.134 2008/08/17 01:20:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -725,18 +725,31 @@ hash_ok_operator(OpExpr *expr)
/*
* convert_ANY_sublink_to_join: can we convert an ANY SubLink to a join?
*
- * The caller has found an ANY SubLink at the top level of WHERE, but has not
- * checked the properties of the SubLink further. Decide whether it is
- * appropriate to process this SubLink in join style. If not, return NULL.
- * If so, build the qual clause(s) to replace the SubLink, and return them.
- * The qual clauses are wrapped in a FlattenedSubLink node to help later
- * processing place them properly.
+ * 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.
+ *
+ * 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.
+ * (We must restrict this to avoid changing the semantics when a sublink
+ * 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.
*
* Side effects of a successful conversion include adding the SubLink's
* subselect to the query's rangetable.
*/
-Node *
-convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink)
+bool
+convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink,
+ Relids available_rels,
+ Node **new_qual, List **fromlist)
{
Query *parse = root->parse;
Query *subselect = (Query *) sublink->subselect;
@@ -755,7 +768,7 @@ convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink)
* higher levels should be okay, though.)
*/
if (contain_vars_of_level((Node *) subselect, 1))
- return NULL;
+ return false;
/*
* The test expression must contain some Vars of the current query,
@@ -764,16 +777,22 @@ convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink)
*/
left_varnos = pull_varnos(sublink->testexpr);
if (bms_is_empty(left_varnos))
- return NULL;
+ return false;
+
+ /*
+ * However, it can't refer to anything outside available_rels.
+ */
+ if (!bms_is_subset(left_varnos, available_rels))
+ return false;
/*
* The combining operators and left-hand expressions mustn't be volatile.
*/
if (contain_volatile_functions(sublink->testexpr))
- return NULL;
+ return false;
/*
- * Okay, pull up the sub-select into top range table and jointree.
+ * Okay, pull up the sub-select into upper range table.
*
* We rely here on the assumption that the outer query has no references
* to the inner (necessarily true, other than the Vars that we build
@@ -786,16 +805,15 @@ convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink)
false);
parse->rtable = lappend(parse->rtable, rte);
rtindex = list_length(parse->rtable);
- rtr = makeNode(RangeTblRef);
- rtr->rtindex = rtindex;
/*
- * We assume it's okay to add the pulled-up subquery to the topmost FROM
- * list. This should be all right for ANY clauses appearing in WHERE
- * or in upper-level plain JOIN/ON clauses. ANYs appearing below any
- * outer joins couldn't be placed there, however.
+ * 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.
*/
- parse->jointree->fromlist = lappend(parse->jointree->fromlist, rtr);
+ rtr = makeNode(RangeTblRef);
+ rtr->rtindex = rtindex;
+ *fromlist = list_make1(rtr);
/*
* Build a list of Vars representing the subselect outputs.
@@ -805,14 +823,14 @@ convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink)
rtindex);
/*
- * Build the result qual expression, replacing Params with these Vars.
+ * Build the replacement qual expression, replacing Params with these Vars.
*/
quals = (Expr *) convert_testexpr(root,
sublink->testexpr,
subquery_vars);
/*
- * Now build the FlattenedSubLink node.
+ * And finally, build the FlattenedSubLink node.
*/
fslink = makeNode(FlattenedSubLink);
fslink->jointype = JOIN_SEMI;
@@ -820,7 +838,9 @@ convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink)
fslink->righthand = bms_make_singleton(rtindex);
fslink->quals = quals;
- return (Node *) fslink;
+ *new_qual = (Node *) fslink;
+
+ return true;
}
/*
@@ -883,20 +903,15 @@ simplify_EXISTS_query(Query *query)
/*
* convert_EXISTS_sublink_to_join: can we convert an EXISTS SubLink to a join?
*
- * The caller has found an EXISTS SubLink at the top level of WHERE, or just
- * underneath a NOT, but has not checked the properties of the SubLink
- * further. Decide whether it is appropriate to process this SubLink in join
- * style. If not, return NULL. If so, build the qual clause(s) to replace
- * the SubLink, and return them. (In the NOT case, the returned clauses are
- * intended to replace the NOT as well.) The qual clauses are wrapped in a
- * FlattenedSubLink node to help later processing place them properly.
- *
- * Side effects of a successful conversion include adding the SubLink's
- * subselect to the query's rangetable.
+ * 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".
*/
-Node *
+bool
convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink,
- bool under_not)
+ bool under_not,
+ Relids available_rels,
+ Node **new_qual, List **fromlist)
{
Query *parse = root->parse;
Query *subselect = (Query *) sublink->subselect;
@@ -924,7 +939,7 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink,
* us with noplace to evaluate the targetlist.
*/
if (!simplify_EXISTS_query(subselect))
- return NULL;
+ return false;
/*
* Separate out the WHERE clause. (We could theoretically also remove
@@ -939,20 +954,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 NULL;
+ return false;
/*
* 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 NULL;
+ return false;
/*
* We don't risk optimizing if the WHERE clause is volatile, either.
*/
if (contain_volatile_functions(whereClause))
- return NULL;
+ return false;
/*
* Also disallow SubLinks within the WHERE clause. (XXX this could
@@ -960,10 +975,10 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink,
* below, and it doesn't seem worth worrying about in a first pass.)
*/
if (contain_subplans(whereClause))
- return NULL;
+ return false;
/*
- * Okay, pull up the sub-select into top range table and jointree.
+ * Prepare to pull up the sub-select into top range table.
*
* We rely here on the assumption that the outer query has no references
* to the inner (necessarily true). Therefore this is a lot easier than
@@ -973,7 +988,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 append the fromlist to our own.
+ * append the rtable to our own and attach the fromlist to our own.
* But first, adjust all level-zero varnos in the subquery to account
* for the rtable merger.
*/
@@ -1007,24 +1022,29 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink,
bms_free(clause_varnos);
Assert(!bms_is_empty(left_varnos));
- /* Also identify all the rels syntactically within the subselect */
- subselect_varnos = get_relids_in_jointree((Node *) subselect->jointree);
+ /*
+ * 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_subset(right_varnos, subselect_varnos));
/* Now we can attach the modified subquery rtable to the parent */
parse->rtable = list_concat(parse->rtable, subselect->rtable);
/*
- * We assume it's okay to add the pulled-up subquery to the topmost FROM
- * list. This should be all right for EXISTS clauses appearing in WHERE
- * or in upper-level plain JOIN/ON clauses. EXISTS appearing below any
- * outer joins couldn't be placed there, however.
+ * Pass back the subquery fromlist to be attached to upper jointree
+ * in a suitable place.
*/
- parse->jointree->fromlist = list_concat(parse->jointree->fromlist,
- subselect->jointree->fromlist);
+ *fromlist = subselect->jointree->fromlist;
/*
- * Now build the FlattenedSubLink node.
+ * And finally, build the FlattenedSubLink node.
*/
fslink = makeNode(FlattenedSubLink);
fslink->jointype = under_not ? JOIN_ANTI : JOIN_SEMI;
@@ -1032,7 +1052,9 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink,
fslink->righthand = subselect_varnos;
fslink->quals = (Expr *) whereClause;
- return (Node *) fslink;
+ *new_qual = (Node *) fslink;
+
+ return true;
}
/*