Commit 19e34b62 authored by Tom Lane's avatar Tom Lane

Improve sublink pullup code to handle ANY/EXISTS sublinks that are at top

level of a JOIN/ON clause, not only at top level of WHERE.  (However, we
can't do this in an outer join's ON clause, unless the ANY/EXISTS refers
only to the nullable side of the outer join, so that it can effectively
be pushed down into the nullable side.)  Per request from Kevin Grittner.

In passing, fix a bug in the initial implementation of EXISTS pullup:
it would Assert if the EXIST's WHERE clause used a join alias variable.
Since we haven't yet flattened join aliases when this transformation
happens, it's necessary to include join relids in the computed set of
RHS relids.
parent 909346ef
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/initsplan.c,v 1.141 2008/08/14 18:47:59 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/plan/initsplan.c,v 1.142 2008/08/17 01:19:59 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -778,7 +778,9 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause, ...@@ -778,7 +778,9 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
root->hasPseudoConstantQuals = true; root->hasPseudoConstantQuals = true;
/* if not below outer join, push it to top of tree */ /* if not below outer join, push it to top of tree */
if (!below_outer_join) if (!below_outer_join)
relids = get_relids_in_jointree((Node *) root->parse->jointree); relids =
get_relids_in_jointree((Node *) root->parse->jointree,
false);
} }
} }
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.241 2008/08/14 18:47:59 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.242 2008/08/17 01:19:59 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -268,14 +268,13 @@ subquery_planner(PlannerGlobal *glob, Query *parse, ...@@ -268,14 +268,13 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
root->append_rel_list = NIL; root->append_rel_list = NIL;
/* /*
* Look for ANY and EXISTS SubLinks at the top level of WHERE, and try to * Look for ANY and EXISTS SubLinks in WHERE and JOIN/ON clauses, and try
* transform them into joins. Note that this step only handles SubLinks * to transform them into joins. Note that this step does not descend
* originally at top level of WHERE; if we pull up any subqueries below, * into subqueries; if we pull up any subqueries below, their SubLinks are
* their SubLinks are processed just before pulling them up. * processed just before pulling them up.
*/ */
if (parse->hasSubLinks) if (parse->hasSubLinks)
parse->jointree->quals = pull_up_sublinks(root, pull_up_sublinks(root);
parse->jointree->quals);
/* /*
* Scan the rangetable for set-returning functions, and inline them * Scan the rangetable for set-returning functions, and inline them
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * 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) ...@@ -725,18 +725,31 @@ hash_ok_operator(OpExpr *expr)
/* /*
* convert_ANY_sublink_to_join: can we convert an ANY SubLink to a join? * 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 * The caller has found an ANY SubLink at the top level of one of the query's
* checked the properties of the SubLink further. Decide whether it is * qual clauses, but has not checked the properties of the SubLink further.
* appropriate to process this SubLink in join style. If not, return NULL. * Decide whether it is appropriate to process this SubLink in join style.
* If so, build the qual clause(s) to replace the SubLink, and return them. * Return TRUE if so, FALSE if the SubLink cannot be converted.
* The qual clauses are wrapped in a FlattenedSubLink node to help later *
* processing place them properly. * 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 * Side effects of a successful conversion include adding the SubLink's
* subselect to the query's rangetable. * subselect to the query's rangetable.
*/ */
Node * bool
convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink) convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink,
Relids available_rels,
Node **new_qual, List **fromlist)
{ {
Query *parse = root->parse; Query *parse = root->parse;
Query *subselect = (Query *) sublink->subselect; Query *subselect = (Query *) sublink->subselect;
...@@ -755,7 +768,7 @@ convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink) ...@@ -755,7 +768,7 @@ convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink)
* higher levels should be okay, though.) * higher levels should be okay, though.)
*/ */
if (contain_vars_of_level((Node *) subselect, 1)) if (contain_vars_of_level((Node *) subselect, 1))
return NULL; return false;
/* /*
* The test expression must contain some Vars of the current query, * The test expression must contain some Vars of the current query,
...@@ -764,16 +777,22 @@ convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink) ...@@ -764,16 +777,22 @@ convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink)
*/ */
left_varnos = pull_varnos(sublink->testexpr); left_varnos = pull_varnos(sublink->testexpr);
if (bms_is_empty(left_varnos)) 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. * The combining operators and left-hand expressions mustn't be volatile.
*/ */
if (contain_volatile_functions(sublink->testexpr)) 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 * 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 * 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) ...@@ -786,16 +805,15 @@ convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink)
false); false);
parse->rtable = lappend(parse->rtable, rte); parse->rtable = lappend(parse->rtable, rte);
rtindex = list_length(parse->rtable); 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 * Form a RangeTblRef for the pulled-up sub-select. This must be added
* list. This should be all right for ANY clauses appearing in WHERE * to the upper jointree, but it is caller's responsibility to figure
* or in upper-level plain JOIN/ON clauses. ANYs appearing below any * out where.
* outer joins couldn't be placed there, however.
*/ */
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. * Build a list of Vars representing the subselect outputs.
...@@ -805,14 +823,14 @@ convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink) ...@@ -805,14 +823,14 @@ convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink)
rtindex); 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, quals = (Expr *) convert_testexpr(root,
sublink->testexpr, sublink->testexpr,
subquery_vars); subquery_vars);
/* /*
* Now build the FlattenedSubLink node. * And finally, build the FlattenedSubLink node.
*/ */
fslink = makeNode(FlattenedSubLink); fslink = makeNode(FlattenedSubLink);
fslink->jointype = JOIN_SEMI; fslink->jointype = JOIN_SEMI;
...@@ -820,7 +838,9 @@ convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink) ...@@ -820,7 +838,9 @@ convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink)
fslink->righthand = bms_make_singleton(rtindex); fslink->righthand = bms_make_singleton(rtindex);
fslink->quals = quals; fslink->quals = quals;
return (Node *) fslink; *new_qual = (Node *) fslink;
return true;
} }
/* /*
...@@ -883,20 +903,15 @@ simplify_EXISTS_query(Query *query) ...@@ -883,20 +903,15 @@ simplify_EXISTS_query(Query *query)
/* /*
* convert_EXISTS_sublink_to_join: can we convert an EXISTS SubLink to a join? * 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 * The API of this function is identical to convert_ANY_sublink_to_join's,
* underneath a NOT, but has not checked the properties of the SubLink * except that we also support the case where the caller has found NOT EXISTS,
* further. Decide whether it is appropriate to process this SubLink in join * so we need an additional input parameter "under_not".
* 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.
*/ */
Node * bool
convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, 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 *parse = root->parse;
Query *subselect = (Query *) sublink->subselect; Query *subselect = (Query *) sublink->subselect;
...@@ -924,7 +939,7 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, ...@@ -924,7 +939,7 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink,
* us with noplace to evaluate the targetlist. * us with noplace to evaluate the targetlist.
*/ */
if (!simplify_EXISTS_query(subselect)) if (!simplify_EXISTS_query(subselect))
return NULL; return false;
/* /*
* Separate out the WHERE clause. (We could theoretically also remove * Separate out the WHERE clause. (We could theoretically also remove
...@@ -939,20 +954,20 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, ...@@ -939,20 +954,20 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink,
* query. (Vars of higher levels should be okay, though.) * query. (Vars of higher levels should be okay, though.)
*/ */
if (contain_vars_of_level((Node *) subselect, 1)) 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 * On the other hand, the WHERE clause must contain some Vars of the
* parent query, else it's not gonna be a join. * parent query, else it's not gonna be a join.
*/ */
if (!contain_vars_of_level(whereClause, 1)) if (!contain_vars_of_level(whereClause, 1))
return NULL; return false;
/* /*
* We don't risk optimizing if the WHERE clause is volatile, either. * We don't risk optimizing if the WHERE clause is volatile, either.
*/ */
if (contain_volatile_functions(whereClause)) if (contain_volatile_functions(whereClause))
return NULL; return false;
/* /*
* Also disallow SubLinks within the WHERE clause. (XXX this could * Also disallow SubLinks within the WHERE clause. (XXX this could
...@@ -960,10 +975,10 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, ...@@ -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.) * below, and it doesn't seem worth worrying about in a first pass.)
*/ */
if (contain_subplans(whereClause)) 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 * 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 * 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, ...@@ -973,7 +988,7 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink,
* to do. The machinations of simplify_EXISTS_query ensured that there * to do. The machinations of simplify_EXISTS_query ensured that there
* is nothing interesting in the subquery except an rtable and jointree, * is nothing interesting in the subquery except an rtable and jointree,
* and even the jointree FromExpr no longer has quals. So we can just * 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 * But first, adjust all level-zero varnos in the subquery to account
* for the rtable merger. * for the rtable merger.
*/ */
...@@ -1007,24 +1022,29 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, ...@@ -1007,24 +1022,29 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink,
bms_free(clause_varnos); bms_free(clause_varnos);
Assert(!bms_is_empty(left_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)); Assert(bms_is_subset(right_varnos, subselect_varnos));
/* Now we can attach the modified subquery rtable to the parent */ /* Now we can attach the modified subquery rtable to the parent */
parse->rtable = list_concat(parse->rtable, subselect->rtable); parse->rtable = list_concat(parse->rtable, subselect->rtable);
/* /*
* We assume it's okay to add the pulled-up subquery to the topmost FROM * Pass back the subquery fromlist to be attached to upper jointree
* list. This should be all right for EXISTS clauses appearing in WHERE * in a suitable place.
* or in upper-level plain JOIN/ON clauses. EXISTS appearing below any
* outer joins couldn't be placed there, however.
*/ */
parse->jointree->fromlist = list_concat(parse->jointree->fromlist, *fromlist = subselect->jointree->fromlist;
subselect->jointree->fromlist);
/* /*
* Now build the FlattenedSubLink node. * And finally, build the FlattenedSubLink node.
*/ */
fslink = makeNode(FlattenedSubLink); fslink = makeNode(FlattenedSubLink);
fslink->jointype = under_not ? JOIN_ANTI : JOIN_SEMI; fslink->jointype = under_not ? JOIN_ANTI : JOIN_SEMI;
...@@ -1032,7 +1052,9 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, ...@@ -1032,7 +1052,9 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink,
fslink->righthand = subselect_varnos; fslink->righthand = subselect_varnos;
fslink->quals = (Expr *) whereClause; fslink->quals = (Expr *) whereClause;
return (Node *) fslink; *new_qual = (Node *) fslink;
return true;
} }
/* /*
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.52 2008/08/14 20:31:29 heikki Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.53 2008/08/17 01:20:00 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -40,6 +40,10 @@ typedef struct reduce_outer_joins_state ...@@ -40,6 +40,10 @@ typedef struct reduce_outer_joins_state
List *sub_states; /* List of states for subtree components */ List *sub_states; /* List of states for subtree components */
} reduce_outer_joins_state; } reduce_outer_joins_state;
static Node *pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode,
Relids *relids);
static Node *pull_up_sublinks_qual_recurse(PlannerInfo *root, Node *node,
Relids available_rels, List **fromlist);
static Node *pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, static Node *pull_up_simple_subquery(PlannerInfo *root, Node *jtnode,
RangeTblEntry *rte, RangeTblEntry *rte,
bool below_outer_join, bool below_outer_join,
...@@ -76,51 +80,228 @@ static Node *find_jointree_node_for_rel(Node *jtnode, int relid); ...@@ -76,51 +80,228 @@ static Node *find_jointree_node_for_rel(Node *jtnode, int relid);
/* /*
* pull_up_sublinks * pull_up_sublinks
* Attempt to pull up top-level ANY and EXISTS SubLinks to be treated * Attempt to pull up ANY and EXISTS SubLinks to be treated as
* as semijoins or anti-semijoins. * semijoins or anti-semijoins.
* *
* A clause "foo op ANY (sub-SELECT)" appearing at the top level of WHERE * A clause "foo op ANY (sub-SELECT)" can be processed by pulling the
* can be processed by pulling the sub-SELECT up to become a rangetable entry * sub-SELECT up to become a rangetable entry and treating the implied
* and handling the implied comparisons as quals of a semijoin. * comparisons as quals of a semijoin. However, this optimization *only*
* This optimization *only* works at the top level of WHERE, because * works at the top level of WHERE or a JOIN/ON clause, because we cannot
* it cannot distinguish whether the ANY ought to return FALSE or NULL in * distinguish whether the ANY ought to return FALSE or NULL in cases
* cases involving NULL inputs. Similarly, EXISTS and NOT EXISTS clauses * involving NULL inputs. Also, in an outer join's ON clause we can only
* can be handled by pulling up the sub-SELECT and creating a semijoin * do this if the sublink is degenerate (ie, references only the nullable
* or anti-semijoin respectively. * side of the join). In that case we can effectively push the semijoin
* down into the nullable side of the join. If the sublink references any
* nonnullable-side variables then it would have to be evaluated as part
* of the outer join, which makes things way too complicated.
*
* Under similar conditions, EXISTS and NOT EXISTS clauses can be handled
* by pulling up the sub-SELECT and creating a semijoin or anti-semijoin.
* *
* This routine searches for such clauses and does the necessary parsetree * This routine searches for such clauses and does the necessary parsetree
* transformations if any are found. * transformations if any are found.
* *
* This routine has to run before preprocess_expression(), so the WHERE * This routine has to run before preprocess_expression(), so the quals
* clause is not yet reduced to implicit-AND format. That means we need * clauses are not yet reduced to implicit-AND format. That means we need
* to recursively search through explicit AND clauses, which are * to recursively search through explicit AND clauses, which are
* probably only binary ANDs. We stop as soon as we hit a non-AND item. * probably only binary ANDs. We stop as soon as we hit a non-AND item.
*/
void
pull_up_sublinks(PlannerInfo *root)
{
Relids relids;
/* Begin recursion through the jointree */
root->parse->jointree = (FromExpr *)
pull_up_sublinks_jointree_recurse(root,
(Node *) root->parse->jointree,
&relids);
}
/*
* Recurse through jointree nodes for pull_up_sublinks()
* *
* Returns the possibly-modified version of the given qual-tree node. * In addition to returning the possibly-modified jointree node, we return
* There may be side-effects on the query's rtable and jointree, too. * a relids set of the contained rels into *relids.
*/ */
Node * static Node *
pull_up_sublinks(PlannerInfo *root, Node *node) pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode,
Relids *relids)
{
if (jtnode == NULL)
{
*relids = NULL;
}
else if (IsA(jtnode, RangeTblRef))
{
int varno = ((RangeTblRef *) jtnode)->rtindex;
*relids = bms_make_singleton(varno);
/* jtnode is returned unmodified */
}
else if (IsA(jtnode, FromExpr))
{
FromExpr *f = (FromExpr *) jtnode;
List *newfromlist = NIL;
Node *newquals;
List *subfromlist = NIL;
Relids frelids = NULL;
ListCell *l;
/* First, recurse to process children and collect their relids */
foreach(l, f->fromlist)
{
Node *newchild;
Relids childrelids;
newchild = pull_up_sublinks_jointree_recurse(root,
lfirst(l),
&childrelids);
newfromlist = lappend(newfromlist, newchild);
frelids = bms_join(frelids, childrelids);
}
/* Now process qual --- all children are available for use */
newquals = pull_up_sublinks_qual_recurse(root, f->quals, frelids,
&subfromlist);
/* Any pulled-up subqueries can just be attached to the fromlist */
newfromlist = list_concat(newfromlist, subfromlist);
/*
* Although we could include the pulled-up subqueries in the returned
* relids, there's no need since upper quals couldn't refer to their
* outputs anyway.
*/
*relids = frelids;
jtnode = (Node *) makeFromExpr(newfromlist, newquals);
}
else if (IsA(jtnode, JoinExpr))
{
JoinExpr *j;
Relids leftrelids;
Relids rightrelids;
List *subfromlist = NIL;
/*
* Make a modifiable copy of join node, but don't bother copying
* its subnodes (yet).
*/
j = (JoinExpr *) palloc(sizeof(JoinExpr));
memcpy(j, jtnode, sizeof(JoinExpr));
/* Recurse to process children and collect their relids */
j->larg = pull_up_sublinks_jointree_recurse(root, j->larg,
&leftrelids);
j->rarg = pull_up_sublinks_jointree_recurse(root, j->rarg,
&rightrelids);
/*
* Now process qual, showing appropriate child relids as available,
* and then attach any pulled-up jointree items at the right place.
* The pulled-up items must go below where the quals that refer to
* them will be placed. Since the JoinExpr itself can only handle
* two child nodes, we hack up a valid jointree by inserting dummy
* FromExprs that have no quals. These should get flattened out
* during deconstruct_recurse(), so they won't impose any extra
* overhead.
*/
switch (j->jointype)
{
case JOIN_INNER:
j->quals = pull_up_sublinks_qual_recurse(root, j->quals,
bms_union(leftrelids,
rightrelids),
&subfromlist);
/* We arbitrarily put pulled-up subqueries into right child */
if (subfromlist)
j->rarg = (Node *) makeFromExpr(lcons(j->rarg,
subfromlist),
NULL);
break;
case JOIN_LEFT:
j->quals = pull_up_sublinks_qual_recurse(root, j->quals,
rightrelids,
&subfromlist);
/* Any pulled-up subqueries must go into right child */
if (subfromlist)
j->rarg = (Node *) makeFromExpr(lcons(j->rarg,
subfromlist),
NULL);
break;
case JOIN_FULL:
/* can't do anything with full-join quals */
break;
case JOIN_RIGHT:
j->quals = pull_up_sublinks_qual_recurse(root, j->quals,
leftrelids,
&subfromlist);
/* Any pulled-up subqueries must go into left child */
if (subfromlist)
j->larg = (Node *) makeFromExpr(lcons(j->larg,
subfromlist),
NULL);
break;
default:
elog(ERROR, "unrecognized join type: %d",
(int) j->jointype);
break;
}
/*
* Although we could include the pulled-up subqueries in the returned
* relids, there's no need since upper quals couldn't refer to their
* outputs anyway. But we *do* need to include the join's own rtindex
* because we haven't yet collapsed join alias variables, so upper
* levels would mistakenly think they couldn't use references to this
* join.
*/
*relids = bms_add_member(bms_join(leftrelids, rightrelids),
j->rtindex);
jtnode = (Node *) j;
}
else
elog(ERROR, "unrecognized node type: %d",
(int) nodeTag(jtnode));
return jtnode;
}
/*
* Recurse through top-level qual nodes for pull_up_sublinks()
*
* Caller must have initialized *fromlist to NIL. We append any new
* jointree items to that list.
*/
static Node *
pull_up_sublinks_qual_recurse(PlannerInfo *root, Node *node,
Relids available_rels, List **fromlist)
{ {
if (node == NULL) if (node == NULL)
return NULL; return NULL;
if (IsA(node, SubLink)) if (IsA(node, SubLink))
{ {
SubLink *sublink = (SubLink *) node; SubLink *sublink = (SubLink *) node;
Node *subst; Node *new_qual;
List *new_fromlist;
/* Is it a convertible ANY or EXISTS clause? */ /* Is it a convertible ANY or EXISTS clause? */
if (sublink->subLinkType == ANY_SUBLINK) if (sublink->subLinkType == ANY_SUBLINK)
{ {
subst = convert_ANY_sublink_to_join(root, sublink); if (convert_ANY_sublink_to_join(root, sublink,
if (subst) available_rels,
return subst; &new_qual, &new_fromlist))
{
*fromlist = list_concat(*fromlist, new_fromlist);
return new_qual;
}
} }
else if (sublink->subLinkType == EXISTS_SUBLINK) else if (sublink->subLinkType == EXISTS_SUBLINK)
{ {
subst = convert_EXISTS_sublink_to_join(root, sublink, false); if (convert_EXISTS_sublink_to_join(root, sublink, false,
if (subst) available_rels,
return subst; &new_qual, &new_fromlist))
{
*fromlist = list_concat(*fromlist, new_fromlist);
return new_qual;
}
} }
/* Else return it unmodified */ /* Else return it unmodified */
return node; return node;
...@@ -129,15 +310,20 @@ pull_up_sublinks(PlannerInfo *root, Node *node) ...@@ -129,15 +310,20 @@ pull_up_sublinks(PlannerInfo *root, Node *node)
{ {
/* If the immediate argument of NOT is EXISTS, try to convert */ /* If the immediate argument of NOT is EXISTS, try to convert */
SubLink *sublink = (SubLink *) get_notclausearg((Expr *) node); SubLink *sublink = (SubLink *) get_notclausearg((Expr *) node);
Node *subst; Node *new_qual;
List *new_fromlist;
if (sublink && IsA(sublink, SubLink)) if (sublink && IsA(sublink, SubLink))
{ {
if (sublink->subLinkType == EXISTS_SUBLINK) if (sublink->subLinkType == EXISTS_SUBLINK)
{ {
subst = convert_EXISTS_sublink_to_join(root, sublink, true); if (convert_EXISTS_sublink_to_join(root, sublink, true,
if (subst) available_rels,
return subst; &new_qual, &new_fromlist))
{
*fromlist = list_concat(*fromlist, new_fromlist);
return new_qual;
}
} }
} }
/* Else return it unmodified */ /* Else return it unmodified */
...@@ -145,6 +331,7 @@ pull_up_sublinks(PlannerInfo *root, Node *node) ...@@ -145,6 +331,7 @@ pull_up_sublinks(PlannerInfo *root, Node *node)
} }
if (and_clause(node)) if (and_clause(node))
{ {
/* Recurse into AND clause */
List *newclauses = NIL; List *newclauses = NIL;
ListCell *l; ListCell *l;
...@@ -153,7 +340,10 @@ pull_up_sublinks(PlannerInfo *root, Node *node) ...@@ -153,7 +340,10 @@ pull_up_sublinks(PlannerInfo *root, Node *node)
Node *oldclause = (Node *) lfirst(l); Node *oldclause = (Node *) lfirst(l);
newclauses = lappend(newclauses, newclauses = lappend(newclauses,
pull_up_sublinks(root, oldclause)); pull_up_sublinks_qual_recurse(root,
oldclause,
available_rels,
fromlist));
} }
return (Node *) make_andclause(newclauses); return (Node *) make_andclause(newclauses);
} }
...@@ -383,12 +573,11 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, ...@@ -383,12 +573,11 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
subroot->append_rel_list = NIL; subroot->append_rel_list = NIL;
/* /*
* Pull up any SubLinks within the subquery's WHERE, so that we don't * Pull up any SubLinks within the subquery's quals, so that we don't
* leave unoptimized SubLinks behind. * leave unoptimized SubLinks behind.
*/ */
if (subquery->hasSubLinks) if (subquery->hasSubLinks)
subquery->jointree->quals = pull_up_sublinks(subroot, pull_up_sublinks(subroot);
subquery->jointree->quals);
/* /*
* Similarly, inline any set-returning functions in its rangetable. * Similarly, inline any set-returning functions in its rangetable.
...@@ -516,7 +705,7 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, ...@@ -516,7 +705,7 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
{ {
Relids subrelids; Relids subrelids;
subrelids = get_relids_in_jointree((Node *) subquery->jointree); subrelids = get_relids_in_jointree((Node *) subquery->jointree, false);
fix_flattened_sublink_relids((Node *) parse, varno, subrelids); fix_flattened_sublink_relids((Node *) parse, varno, subrelids);
fix_append_rel_relids(root->append_rel_list, varno, subrelids); fix_append_rel_relids(root->append_rel_list, varno, subrelids);
} }
...@@ -1484,10 +1673,13 @@ fix_append_rel_relids(List *append_rel_list, int varno, Relids subrelids) ...@@ -1484,10 +1673,13 @@ fix_append_rel_relids(List *append_rel_list, int varno, Relids subrelids)
} }
/* /*
* get_relids_in_jointree: get set of base RT indexes present in a jointree * get_relids_in_jointree: get set of RT indexes present in a jointree
*
* If include_joins is true, join RT indexes are included; if false,
* only base rels are included.
*/ */
Relids Relids
get_relids_in_jointree(Node *jtnode) get_relids_in_jointree(Node *jtnode, bool include_joins)
{ {
Relids result = NULL; Relids result = NULL;
...@@ -1507,16 +1699,19 @@ get_relids_in_jointree(Node *jtnode) ...@@ -1507,16 +1699,19 @@ get_relids_in_jointree(Node *jtnode)
foreach(l, f->fromlist) foreach(l, f->fromlist)
{ {
result = bms_join(result, result = bms_join(result,
get_relids_in_jointree(lfirst(l))); get_relids_in_jointree(lfirst(l),
include_joins));
} }
} }
else if (IsA(jtnode, JoinExpr)) else if (IsA(jtnode, JoinExpr))
{ {
JoinExpr *j = (JoinExpr *) jtnode; JoinExpr *j = (JoinExpr *) jtnode;
/* join's own RT index is not wanted in result */ result = get_relids_in_jointree(j->larg, include_joins);
result = get_relids_in_jointree(j->larg); result = bms_join(result,
result = bms_join(result, get_relids_in_jointree(j->rarg)); get_relids_in_jointree(j->rarg, include_joins));
if (include_joins)
result = bms_add_member(result, j->rtindex);
} }
else else
elog(ERROR, "unrecognized node type: %d", elog(ERROR, "unrecognized node type: %d",
...@@ -1536,7 +1731,7 @@ get_relids_for_join(PlannerInfo *root, int joinrelid) ...@@ -1536,7 +1731,7 @@ get_relids_for_join(PlannerInfo *root, int joinrelid)
joinrelid); joinrelid);
if (!jtnode) if (!jtnode)
elog(ERROR, "could not find join node %d", joinrelid); elog(ERROR, "could not find join node %d", joinrelid);
return get_relids_in_jointree(jtnode); return get_relids_in_jointree(jtnode, false);
} }
/* /*
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/optimizer/prep.h,v 1.61 2008/08/14 18:48:00 tgl Exp $ * $PostgreSQL: pgsql/src/include/optimizer/prep.h,v 1.62 2008/08/17 01:20:00 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -21,12 +21,12 @@ ...@@ -21,12 +21,12 @@
/* /*
* prototypes for prepjointree.c * prototypes for prepjointree.c
*/ */
extern Node *pull_up_sublinks(PlannerInfo *root, Node *node); extern void pull_up_sublinks(PlannerInfo *root);
extern void inline_set_returning_functions(PlannerInfo *root); extern void inline_set_returning_functions(PlannerInfo *root);
extern Node *pull_up_subqueries(PlannerInfo *root, Node *jtnode, extern Node *pull_up_subqueries(PlannerInfo *root, Node *jtnode,
bool below_outer_join, bool append_rel_member); bool below_outer_join, bool append_rel_member);
extern void reduce_outer_joins(PlannerInfo *root); extern void reduce_outer_joins(PlannerInfo *root);
extern Relids get_relids_in_jointree(Node *jtnode); extern Relids get_relids_in_jointree(Node *jtnode, bool include_joins);
extern Relids get_relids_for_join(PlannerInfo *root, int joinrelid); extern Relids get_relids_for_join(PlannerInfo *root, int joinrelid);
/* /*
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/optimizer/subselect.h,v 1.32 2008/08/14 18:48:00 tgl Exp $ * $PostgreSQL: pgsql/src/include/optimizer/subselect.h,v 1.33 2008/08/17 01:20:00 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -15,9 +15,13 @@ ...@@ -15,9 +15,13 @@
#include "nodes/plannodes.h" #include "nodes/plannodes.h"
#include "nodes/relation.h" #include "nodes/relation.h"
extern Node *convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink); extern bool convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink,
extern Node *convert_EXISTS_sublink_to_join(PlannerInfo *root, Relids available_rels,
SubLink *sublink, bool under_not); Node **new_qual, List **fromlist);
extern bool convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink,
bool under_not,
Relids available_rels,
Node **new_qual, List **fromlist);
extern Node *SS_replace_correlation_vars(PlannerInfo *root, Node *expr); extern Node *SS_replace_correlation_vars(PlannerInfo *root, Node *expr);
extern Node *SS_process_sublinks(PlannerInfo *root, Node *expr, bool isQual); extern Node *SS_process_sublinks(PlannerInfo *root, Node *expr, bool isQual);
extern void SS_finalize_plan(PlannerInfo *root, Plan *plan, extern void SS_finalize_plan(PlannerInfo *root, Plan *plan,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment