mirror of
https://github.com/postgres/postgres.git
synced 2025-09-08 00:47:37 +03:00
Get rid of the rather fuzzily defined FlattenedSubLink node type in favor of
making pull_up_sublinks() construct a full-blown JoinExpr tree representation of IN/EXISTS SubLinks that it is able to convert to semi or anti joins. This makes pull_up_sublinks() a shade more complex, but the gain in semantic clarity is worth it. I still have more to do in this area to address the previously-discussed problems, but this commit in itself fixes at least one bug in HEAD, as shown by added regression test case.
This commit is contained in:
@@ -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 */
|
||||
@@ -1044,64 +1048,6 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
|
||||
distribute_restrictinfo_to_rels(root, restrictinfo);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
|
@@ -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;
|
||||
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 */
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
return true;
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@@ -16,7 +16,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.62 2009/01/01 17:23:44 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.63 2009/02/25 03:30:37 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -44,7 +44,7 @@ typedef struct 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);
|
||||
Relids available_rels, Node **jtlink);
|
||||
static Node *pull_up_simple_subquery(PlannerInfo *root, Node *jtnode,
|
||||
RangeTblEntry *rte,
|
||||
bool below_outer_join,
|
||||
@@ -91,7 +91,7 @@ static Node *find_jointree_node_for_rel(Node *jtnode, int relid);
|
||||
* distinguish whether the ANY ought to return FALSE or NULL in cases
|
||||
* involving NULL inputs. Also, in an outer join's ON clause we can only
|
||||
* do this if the sublink is degenerate (ie, references only the nullable
|
||||
* side of the join). In that case we can effectively push the semijoin
|
||||
* side of the join). In that case it is legal to 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.
|
||||
@@ -110,13 +110,22 @@ static Node *find_jointree_node_for_rel(Node *jtnode, int relid);
|
||||
void
|
||||
pull_up_sublinks(PlannerInfo *root)
|
||||
{
|
||||
Node *jtnode;
|
||||
Relids relids;
|
||||
|
||||
/* Begin recursion through the jointree */
|
||||
root->parse->jointree = (FromExpr *)
|
||||
pull_up_sublinks_jointree_recurse(root,
|
||||
(Node *) root->parse->jointree,
|
||||
&relids);
|
||||
jtnode = pull_up_sublinks_jointree_recurse(root,
|
||||
(Node *) root->parse->jointree,
|
||||
&relids);
|
||||
|
||||
/*
|
||||
* root->parse->jointree must always be a FromExpr, so insert a dummy one
|
||||
* if we got a bare RangeTblRef or JoinExpr out of the recursion.
|
||||
*/
|
||||
if (IsA(jtnode, FromExpr))
|
||||
root->parse->jointree = (FromExpr *) jtnode;
|
||||
else
|
||||
root->parse->jointree = makeFromExpr(list_make1(jtnode), NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -144,9 +153,9 @@ pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode,
|
||||
{
|
||||
FromExpr *f = (FromExpr *) jtnode;
|
||||
List *newfromlist = NIL;
|
||||
Node *newquals;
|
||||
List *subfromlist = NIL;
|
||||
Relids frelids = NULL;
|
||||
FromExpr *newf;
|
||||
Node *jtlink;
|
||||
ListCell *l;
|
||||
|
||||
/* First, recurse to process children and collect their relids */
|
||||
@@ -161,26 +170,32 @@ pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode,
|
||||
newfromlist = lappend(newfromlist, newchild);
|
||||
frelids = bms_join(frelids, childrelids);
|
||||
}
|
||||
/* Build the replacement FromExpr; no quals yet */
|
||||
newf = makeFromExpr(newfromlist, NULL);
|
||||
/* Set up a link representing the rebuilt jointree */
|
||||
jtlink = (Node *) newf;
|
||||
/* 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);
|
||||
newf->quals = pull_up_sublinks_qual_recurse(root, f->quals, frelids,
|
||||
&jtlink);
|
||||
|
||||
/*
|
||||
* Note that the result will be either newf, or a stack of JoinExprs
|
||||
* with newf at the base. We rely on subsequent optimization steps
|
||||
* to flatten this and rearrange the joins as needed.
|
||||
*
|
||||
* 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);
|
||||
jtnode = jtlink;
|
||||
}
|
||||
else if (IsA(jtnode, JoinExpr))
|
||||
{
|
||||
JoinExpr *j;
|
||||
Relids leftrelids;
|
||||
Relids rightrelids;
|
||||
List *subfromlist = NIL;
|
||||
Node *jtlink;
|
||||
|
||||
/*
|
||||
* Make a modifiable copy of join node, but don't bother copying
|
||||
@@ -188,6 +203,7 @@ pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode,
|
||||
*/
|
||||
j = (JoinExpr *) palloc(sizeof(JoinExpr));
|
||||
memcpy(j, jtnode, sizeof(JoinExpr));
|
||||
jtlink = (Node *) j;
|
||||
|
||||
/* Recurse to process children and collect their relids */
|
||||
j->larg = pull_up_sublinks_jointree_recurse(root, j->larg,
|
||||
@@ -197,13 +213,15 @@ pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode,
|
||||
|
||||
/*
|
||||
* 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.
|
||||
* and attach any pulled-up jointree items at the right place.
|
||||
* In the inner-join case we put new JoinExprs above the existing one
|
||||
* (much as for a FromExpr-style join). In outer-join cases the
|
||||
* new JoinExprs must go into the nullable side of the outer join.
|
||||
* The point of the available_rels machinations is to ensure that we
|
||||
* only pull up quals for which that's okay.
|
||||
*
|
||||
* We don't expect to see any pre-existing JOIN_SEMI or JOIN_ANTI
|
||||
* nodes here.
|
||||
*/
|
||||
switch (j->jointype)
|
||||
{
|
||||
@@ -211,22 +229,12 @@ pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode,
|
||||
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);
|
||||
&jtlink);
|
||||
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);
|
||||
&j->rarg);
|
||||
break;
|
||||
case JOIN_FULL:
|
||||
/* can't do anything with full-join quals */
|
||||
@@ -234,12 +242,7 @@ pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode,
|
||||
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);
|
||||
&j->larg);
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "unrecognized join type: %d",
|
||||
@@ -255,9 +258,10 @@ pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode,
|
||||
* 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;
|
||||
*relids = bms_join(leftrelids, rightrelids);
|
||||
if (j->rtindex)
|
||||
*relids = bms_add_member(*relids, j->rtindex);
|
||||
jtnode = jtlink;
|
||||
}
|
||||
else
|
||||
elog(ERROR, "unrecognized node type: %d",
|
||||
@@ -268,40 +272,47 @@ pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *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.
|
||||
* jtlink points to the link in the jointree where any new JoinExprs should be
|
||||
* inserted. If we find multiple pull-up-able SubLinks, they'll get stacked
|
||||
* there in the order we encounter them. We rely on subsequent optimization
|
||||
* to rearrange the stack if appropriate.
|
||||
*/
|
||||
static Node *
|
||||
pull_up_sublinks_qual_recurse(PlannerInfo *root, Node *node,
|
||||
Relids available_rels, List **fromlist)
|
||||
Relids available_rels, Node **jtlink)
|
||||
{
|
||||
if (node == NULL)
|
||||
return NULL;
|
||||
if (IsA(node, SubLink))
|
||||
{
|
||||
SubLink *sublink = (SubLink *) node;
|
||||
Node *new_qual;
|
||||
List *new_fromlist;
|
||||
JoinExpr *j;
|
||||
|
||||
/* Is it a convertible ANY or EXISTS clause? */
|
||||
if (sublink->subLinkType == ANY_SUBLINK)
|
||||
{
|
||||
if (convert_ANY_sublink_to_join(root, sublink,
|
||||
available_rels,
|
||||
&new_qual, &new_fromlist))
|
||||
j = convert_ANY_sublink_to_join(root, sublink,
|
||||
available_rels);
|
||||
if (j)
|
||||
{
|
||||
*fromlist = list_concat(*fromlist, new_fromlist);
|
||||
return new_qual;
|
||||
/* Yes, insert the new join node into the join tree */
|
||||
j->larg = *jtlink;
|
||||
*jtlink = (Node *) j;
|
||||
/* and return NULL representing constant TRUE */
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else if (sublink->subLinkType == EXISTS_SUBLINK)
|
||||
{
|
||||
if (convert_EXISTS_sublink_to_join(root, sublink, false,
|
||||
available_rels,
|
||||
&new_qual, &new_fromlist))
|
||||
j = convert_EXISTS_sublink_to_join(root, sublink, false,
|
||||
available_rels);
|
||||
if (j)
|
||||
{
|
||||
*fromlist = list_concat(*fromlist, new_fromlist);
|
||||
return new_qual;
|
||||
/* Yes, insert the new join node into the join tree */
|
||||
j->larg = *jtlink;
|
||||
*jtlink = (Node *) j;
|
||||
/* and return NULL representing constant TRUE */
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
/* Else return it unmodified */
|
||||
@@ -311,19 +322,21 @@ pull_up_sublinks_qual_recurse(PlannerInfo *root, Node *node,
|
||||
{
|
||||
/* If the immediate argument of NOT is EXISTS, try to convert */
|
||||
SubLink *sublink = (SubLink *) get_notclausearg((Expr *) node);
|
||||
Node *new_qual;
|
||||
List *new_fromlist;
|
||||
JoinExpr *j;
|
||||
|
||||
if (sublink && IsA(sublink, SubLink))
|
||||
{
|
||||
if (sublink->subLinkType == EXISTS_SUBLINK)
|
||||
{
|
||||
if (convert_EXISTS_sublink_to_join(root, sublink, true,
|
||||
available_rels,
|
||||
&new_qual, &new_fromlist))
|
||||
j = convert_EXISTS_sublink_to_join(root, sublink, true,
|
||||
available_rels);
|
||||
if (j)
|
||||
{
|
||||
*fromlist = list_concat(*fromlist, new_fromlist);
|
||||
return new_qual;
|
||||
/* Yes, insert the new join node into the join tree */
|
||||
j->larg = *jtlink;
|
||||
*jtlink = (Node *) j;
|
||||
/* and return NULL representing constant TRUE */
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -339,14 +352,22 @@ pull_up_sublinks_qual_recurse(PlannerInfo *root, Node *node,
|
||||
foreach(l, ((BoolExpr *) node)->args)
|
||||
{
|
||||
Node *oldclause = (Node *) lfirst(l);
|
||||
Node *newclause;
|
||||
|
||||
newclauses = lappend(newclauses,
|
||||
pull_up_sublinks_qual_recurse(root,
|
||||
oldclause,
|
||||
available_rels,
|
||||
fromlist));
|
||||
newclause = pull_up_sublinks_qual_recurse(root,
|
||||
oldclause,
|
||||
available_rels,
|
||||
jtlink);
|
||||
if (newclause)
|
||||
newclauses = lappend(newclauses, newclause);
|
||||
}
|
||||
return (Node *) make_andclause(newclauses);
|
||||
/* We might have got back fewer clauses than we started with */
|
||||
if (newclauses == NIL)
|
||||
return NULL;
|
||||
else if (list_length(newclauses) == 1)
|
||||
return (Node *) linitial(newclauses);
|
||||
else
|
||||
return (Node *) make_andclause(newclauses);
|
||||
}
|
||||
/* Stop if not an AND */
|
||||
return node;
|
||||
@@ -489,6 +510,8 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode,
|
||||
below_outer_join, false);
|
||||
break;
|
||||
case JOIN_LEFT:
|
||||
case JOIN_SEMI:
|
||||
case JOIN_ANTI:
|
||||
j->larg = pull_up_subqueries(root, j->larg,
|
||||
below_outer_join, false);
|
||||
j->rarg = pull_up_subqueries(root, j->rarg,
|
||||
@@ -702,12 +725,12 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
|
||||
parse->rowMarks = list_concat(parse->rowMarks, subquery->rowMarks);
|
||||
|
||||
/*
|
||||
* We also have to fix the relid sets of any FlattenedSubLink and
|
||||
* PlaceHolderVar nodes in the parent query. (This could perhaps be done
|
||||
* by ResolveNew, but it would clutter that routine's API unreasonably.)
|
||||
* Note in particular that any PlaceHolderVar nodes just created by
|
||||
* insert_targetlist_placeholders() will be adjusted, so having created
|
||||
* them with the subquery's varno is correct.
|
||||
* We also have to fix the relid sets of any PlaceHolderVar nodes in the
|
||||
* parent query. (This could perhaps be done by ResolveNew, but it would
|
||||
* clutter that routine's API unreasonably.) Note in particular that any
|
||||
* PlaceHolderVar nodes just created by insert_targetlist_placeholders()
|
||||
* will be adjusted, so having created them with the subquery's varno is
|
||||
* correct.
|
||||
*
|
||||
* Likewise, relids appearing in AppendRelInfo nodes have to be fixed.
|
||||
* We already checked that this won't require introducing multiple
|
||||
@@ -1419,6 +1442,14 @@ reduce_outer_joins_pass2(Node *jtnode,
|
||||
jointype = JOIN_RIGHT;
|
||||
}
|
||||
break;
|
||||
case JOIN_SEMI:
|
||||
case JOIN_ANTI:
|
||||
/*
|
||||
* These could only have been introduced by pull_up_sublinks,
|
||||
* so there's no way that upper quals could refer to their
|
||||
* righthand sides, and no point in checking.
|
||||
*/
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "unrecognized join type: %d",
|
||||
(int) jointype);
|
||||
@@ -1475,14 +1506,15 @@ reduce_outer_joins_pass2(Node *jtnode,
|
||||
}
|
||||
|
||||
/* Apply the jointype change, if any, to both jointree node and RTE */
|
||||
if (jointype != j->jointype)
|
||||
if (rtindex && jointype != j->jointype)
|
||||
{
|
||||
RangeTblEntry *rte = rt_fetch(rtindex, root->parse->rtable);
|
||||
|
||||
Assert(rte->rtekind == RTE_JOIN);
|
||||
Assert(rte->jointype == j->jointype);
|
||||
rte->jointype = j->jointype = jointype;
|
||||
rte->jointype = jointype;
|
||||
}
|
||||
j->jointype = jointype;
|
||||
|
||||
/* Only recurse if there's more to do below here */
|
||||
if (left_state->contains_outer || right_state->contains_outer)
|
||||
@@ -1542,7 +1574,7 @@ reduce_outer_joins_pass2(Node *jtnode,
|
||||
pass_nonnullable_vars = local_nonnullable_vars;
|
||||
pass_forced_null_vars = local_forced_null_vars;
|
||||
}
|
||||
else if (jointype != JOIN_FULL) /* ie, LEFT or ANTI */
|
||||
else if (jointype != JOIN_FULL) /* ie, LEFT/SEMI/ANTI */
|
||||
{
|
||||
/* can't pass local constraints to non-nullable side */
|
||||
pass_nonnullable_rels = nonnullable_rels;
|
||||
@@ -1564,7 +1596,7 @@ reduce_outer_joins_pass2(Node *jtnode,
|
||||
|
||||
if (right_state->contains_outer)
|
||||
{
|
||||
if (jointype != JOIN_FULL) /* ie, INNER, LEFT or ANTI */
|
||||
if (jointype != JOIN_FULL) /* ie, INNER/LEFT/SEMI/ANTI */
|
||||
{
|
||||
/* pass appropriate constraints, per comment above */
|
||||
pass_nonnullable_rels = local_nonnullable_rels;
|
||||
@@ -1595,10 +1627,10 @@ reduce_outer_joins_pass2(Node *jtnode,
|
||||
* substitute_multiple_relids - adjust node relid sets after pulling up
|
||||
* a subquery
|
||||
*
|
||||
* Find any FlattenedSubLink or PlaceHolderVar nodes in the given tree that
|
||||
* reference the pulled-up relid, and change them to reference the replacement
|
||||
* relid(s). We do not need to recurse into subqueries, since no subquery of
|
||||
* the current top query could (yet) contain such a reference.
|
||||
* Find any PlaceHolderVar nodes in the given tree that reference the
|
||||
* pulled-up relid, and change them to reference the replacement relid(s).
|
||||
* We do not need to recurse into subqueries, since no subquery of the current
|
||||
* top query could (yet) contain such a reference.
|
||||
*
|
||||
* NOTE: although this has the form of a walker, we cheat and modify the
|
||||
* nodes in-place. This should be OK since the tree was copied by ResolveNew
|
||||
@@ -1618,26 +1650,6 @@ substitute_multiple_relids_walker(Node *node,
|
||||
{
|
||||
if (node == NULL)
|
||||
return false;
|
||||
if (IsA(node, FlattenedSubLink))
|
||||
{
|
||||
FlattenedSubLink *fslink = (FlattenedSubLink *) node;
|
||||
|
||||
if (bms_is_member(context->varno, fslink->lefthand))
|
||||
{
|
||||
fslink->lefthand = bms_union(fslink->lefthand,
|
||||
context->subrelids);
|
||||
fslink->lefthand = bms_del_member(fslink->lefthand,
|
||||
context->varno);
|
||||
}
|
||||
if (bms_is_member(context->varno, fslink->righthand))
|
||||
{
|
||||
fslink->righthand = bms_union(fslink->righthand,
|
||||
context->subrelids);
|
||||
fslink->righthand = bms_del_member(fslink->righthand,
|
||||
context->varno);
|
||||
}
|
||||
/* fall through to examine children */
|
||||
}
|
||||
if (IsA(node, PlaceHolderVar))
|
||||
{
|
||||
PlaceHolderVar *phv = (PlaceHolderVar *) node;
|
||||
@@ -1757,7 +1769,7 @@ get_relids_in_jointree(Node *jtnode, bool include_joins)
|
||||
result = get_relids_in_jointree(j->larg, include_joins);
|
||||
result = bms_join(result,
|
||||
get_relids_in_jointree(j->rarg, include_joins));
|
||||
if (include_joins)
|
||||
if (include_joins && j->rtindex)
|
||||
result = bms_add_member(result, j->rtindex);
|
||||
}
|
||||
else
|
||||
|
@@ -22,7 +22,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.165 2009/02/06 23:43:23 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.166 2009/02/25 03:30:37 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -1587,23 +1587,6 @@ adjust_appendrel_attrs_mutator(Node *node, AppendRelInfo *context)
|
||||
j->rtindex = context->child_relid;
|
||||
return (Node *) j;
|
||||
}
|
||||
if (IsA(node, FlattenedSubLink))
|
||||
{
|
||||
/* Copy the FlattenedSubLink node with correct mutation of subnodes */
|
||||
FlattenedSubLink *fslink;
|
||||
|
||||
fslink = (FlattenedSubLink *) expression_tree_mutator(node,
|
||||
adjust_appendrel_attrs_mutator,
|
||||
(void *) context);
|
||||
/* now fix FlattenedSubLink's relid sets */
|
||||
fslink->lefthand = adjust_relid_set(fslink->lefthand,
|
||||
context->parent_relid,
|
||||
context->child_relid);
|
||||
fslink->righthand = adjust_relid_set(fslink->righthand,
|
||||
context->parent_relid,
|
||||
context->child_relid);
|
||||
return (Node *) fslink;
|
||||
}
|
||||
if (IsA(node, PlaceHolderVar))
|
||||
{
|
||||
/* Copy the PlaceHolderVar node with correct mutation of subnodes */
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.275 2009/01/09 15:46:10 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.276 2009/02/25 03:30:37 tgl Exp $
|
||||
*
|
||||
* HISTORY
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
@@ -1300,15 +1300,6 @@ find_nonnullable_rels_walker(Node *node, bool top_level)
|
||||
expr->booltesttype == IS_NOT_UNKNOWN))
|
||||
result = find_nonnullable_rels_walker((Node *) expr->arg, false);
|
||||
}
|
||||
else if (IsA(node, FlattenedSubLink))
|
||||
{
|
||||
/* JOIN_SEMI sublinks preserve strictness, but JOIN_ANTI ones don't */
|
||||
FlattenedSubLink *expr = (FlattenedSubLink *) node;
|
||||
|
||||
if (expr->jointype == JOIN_SEMI)
|
||||
result = find_nonnullable_rels_walker((Node *) expr->quals,
|
||||
top_level);
|
||||
}
|
||||
else if (IsA(node, PlaceHolderVar))
|
||||
{
|
||||
PlaceHolderVar *phv = (PlaceHolderVar *) node;
|
||||
@@ -1511,15 +1502,6 @@ find_nonnullable_vars_walker(Node *node, bool top_level)
|
||||
expr->booltesttype == IS_NOT_UNKNOWN))
|
||||
result = find_nonnullable_vars_walker((Node *) expr->arg, false);
|
||||
}
|
||||
else if (IsA(node, FlattenedSubLink))
|
||||
{
|
||||
/* JOIN_SEMI sublinks preserve strictness, but JOIN_ANTI ones don't */
|
||||
FlattenedSubLink *expr = (FlattenedSubLink *) node;
|
||||
|
||||
if (expr->jointype == JOIN_SEMI)
|
||||
result = find_nonnullable_vars_walker((Node *) expr->quals,
|
||||
top_level);
|
||||
}
|
||||
else if (IsA(node, PlaceHolderVar))
|
||||
{
|
||||
PlaceHolderVar *phv = (PlaceHolderVar *) node;
|
||||
@@ -2943,24 +2925,6 @@ eval_const_expressions_mutator(Node *node,
|
||||
newbtest->booltesttype = btest->booltesttype;
|
||||
return (Node *) newbtest;
|
||||
}
|
||||
if (IsA(node, FlattenedSubLink))
|
||||
{
|
||||
FlattenedSubLink *fslink = (FlattenedSubLink *) node;
|
||||
FlattenedSubLink *newfslink;
|
||||
Expr *quals;
|
||||
|
||||
/* Simplify and also canonicalize the arguments */
|
||||
quals = (Expr *) eval_const_expressions_mutator((Node *) fslink->quals,
|
||||
context);
|
||||
quals = canonicalize_qual(quals);
|
||||
|
||||
newfslink = makeNode(FlattenedSubLink);
|
||||
newfslink->jointype = fslink->jointype;
|
||||
newfslink->lefthand = fslink->lefthand;
|
||||
newfslink->righthand = fslink->righthand;
|
||||
newfslink->quals = quals;
|
||||
return (Node *) newfslink;
|
||||
}
|
||||
if (IsA(node, PlaceHolderVar) && context->estimate)
|
||||
{
|
||||
/*
|
||||
|
@@ -14,7 +14,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/util/var.c,v 1.83 2009/01/01 17:23:45 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/util/var.c,v 1.84 2009/02/25 03:30:37 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -766,24 +766,6 @@ flatten_join_alias_vars_mutator(Node *node,
|
||||
/* Recurse in case join input is itself a join */
|
||||
return flatten_join_alias_vars_mutator(newvar, context);
|
||||
}
|
||||
if (IsA(node, FlattenedSubLink))
|
||||
{
|
||||
/* Copy the FlattenedSubLink node with correct mutation of subnodes */
|
||||
FlattenedSubLink *fslink;
|
||||
|
||||
fslink = (FlattenedSubLink *) expression_tree_mutator(node,
|
||||
flatten_join_alias_vars_mutator,
|
||||
(void *) context);
|
||||
/* now fix FlattenedSubLink's relid sets */
|
||||
if (context->sublevels_up == 0)
|
||||
{
|
||||
fslink->lefthand = alias_relid_set(context->root,
|
||||
fslink->lefthand);
|
||||
fslink->righthand = alias_relid_set(context->root,
|
||||
fslink->righthand);
|
||||
}
|
||||
return (Node *) fslink;
|
||||
}
|
||||
if (IsA(node, PlaceHolderVar))
|
||||
{
|
||||
/* Copy the PlaceHolderVar node with correct mutation of subnodes */
|
||||
|
Reference in New Issue
Block a user