summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane2009-02-25 03:30:38 +0000
committerTom Lane2009-02-25 03:30:38 +0000
commit81242b052e573569eb00cd4ee88cc7657093c491 (patch)
tree7419f6ea627ff1895077ba4e6bb56a7ca1706051
parent198053cc73d734716b4e008c14dd3cb6dce58891 (diff)
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.
-rw-r--r--src/backend/nodes/copyfuncs.c19
-rw-r--r--src/backend/nodes/equalfuncs.c14
-rw-r--r--src/backend/nodes/nodeFuncs.c19
-rw-r--r--src/backend/nodes/outfuncs.c14
-rw-r--r--src/backend/optimizer/plan/initsplan.c108
-rw-r--r--src/backend/optimizer/plan/subselect.c166
-rw-r--r--src/backend/optimizer/prep/prepjointree.c224
-rw-r--r--src/backend/optimizer/prep/prepunion.c17
-rw-r--r--src/backend/optimizer/util/clauses.c36
-rw-r--r--src/backend/optimizer/util/var.c18
-rw-r--r--src/backend/rewrite/rewriteManip.c31
-rw-r--r--src/include/nodes/nodes.h1
-rw-r--r--src/include/nodes/primnodes.h6
-rw-r--r--src/include/nodes/relation.h31
-rw-r--r--src/include/optimizer/subselect.h14
-rw-r--r--src/test/regress/expected/join.out19
-rw-r--r--src/test/regress/expected/join_1.out19
-rw-r--r--src/test/regress/sql/join.sql10
18 files changed, 284 insertions, 482 deletions
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index b522bc884d..a6b1621c4b 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -1625,22 +1625,6 @@ _copyRestrictInfo(RestrictInfo *from)
}
/*
- * _copyFlattenedSubLink
- */
-static FlattenedSubLink *
-_copyFlattenedSubLink(FlattenedSubLink *from)
-{
- FlattenedSubLink *newnode = makeNode(FlattenedSubLink);
-
- COPY_SCALAR_FIELD(jointype);
- COPY_BITMAPSET_FIELD(lefthand);
- COPY_BITMAPSET_FIELD(righthand);
- COPY_NODE_FIELD(quals);
-
- return newnode;
-}
-
-/*
* _copyPlaceHolderVar
*/
static PlaceHolderVar *
@@ -3710,9 +3694,6 @@ copyObject(void *from)
case T_RestrictInfo:
retval = _copyRestrictInfo(from);
break;
- case T_FlattenedSubLink:
- retval = _copyFlattenedSubLink(from);
- break;
case T_PlaceHolderVar:
retval = _copyPlaceHolderVar(from);
break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index ac20256c85..6326f3b9f4 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -767,17 +767,6 @@ _equalRestrictInfo(RestrictInfo *a, RestrictInfo *b)
}
static bool
-_equalFlattenedSubLink(FlattenedSubLink *a, FlattenedSubLink *b)
-{
- COMPARE_SCALAR_FIELD(jointype);
- COMPARE_BITMAPSET_FIELD(lefthand);
- COMPARE_BITMAPSET_FIELD(righthand);
- COMPARE_NODE_FIELD(quals);
-
- return true;
-}
-
-static bool
_equalPlaceHolderVar(PlaceHolderVar *a, PlaceHolderVar *b)
{
/*
@@ -2496,9 +2485,6 @@ equal(void *a, void *b)
case T_RestrictInfo:
retval = _equalRestrictInfo(a, b);
break;
- case T_FlattenedSubLink:
- retval = _equalFlattenedSubLink(a, b);
- break;
case T_PlaceHolderVar:
retval = _equalPlaceHolderVar(a, b);
break;
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index f45e0b2b9d..69aa471319 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -1309,14 +1309,6 @@ expression_tree_walker(Node *node,
/* groupClauses are deemed uninteresting */
}
break;
- case T_FlattenedSubLink:
- {
- FlattenedSubLink *fslink = (FlattenedSubLink *) node;
-
- if (walker(fslink->quals, context))
- return true;
- }
- break;
case T_PlaceHolderVar:
return walker(((PlaceHolderVar *) node)->phexpr, context);
case T_AppendRelInfo:
@@ -1972,17 +1964,6 @@ expression_tree_mutator(Node *node,
return (Node *) newnode;
}
break;
- case T_FlattenedSubLink:
- {
- FlattenedSubLink *fslink = (FlattenedSubLink *) node;
- FlattenedSubLink *newnode;
-
- FLATCOPY(newnode, fslink, FlattenedSubLink);
- /* Assume we need not copy the relids bitmapsets */
- MUTATE(newnode->quals, fslink->quals, Expr *);
- return (Node *) newnode;
- }
- break;
case T_PlaceHolderVar:
{
PlaceHolderVar *phv = (PlaceHolderVar *) node;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index a7d71d071f..c11d1ec5cc 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -1631,17 +1631,6 @@ _outInnerIndexscanInfo(StringInfo str, InnerIndexscanInfo *node)
}
static void
-_outFlattenedSubLink(StringInfo str, FlattenedSubLink *node)
-{
- WRITE_NODE_TYPE("FLATTENEDSUBLINK");
-
- WRITE_ENUM_FIELD(jointype, JoinType);
- WRITE_BITMAPSET_FIELD(lefthand);
- WRITE_BITMAPSET_FIELD(righthand);
- WRITE_NODE_FIELD(quals);
-}
-
-static void
_outPlaceHolderVar(StringInfo str, PlaceHolderVar *node)
{
WRITE_NODE_TYPE("PLACEHOLDERVAR");
@@ -2660,9 +2649,6 @@ _outNode(StringInfo str, void *obj)
case T_InnerIndexscanInfo:
_outInnerIndexscanInfo(str, obj);
break;
- case T_FlattenedSubLink:
- _outFlattenedSubLink(str, obj);
- break;
case T_PlaceHolderVar:
_outPlaceHolderVar(str, obj);
break;
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index 1e126a5a12..3e3038f2e0 100644
--- a/src/backend/optimizer/plan/initsplan.c
+++ b/src/backend/optimizer/plan/initsplan.c
@@ -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 76cb7bf653..1cdd01d2e0 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -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;
}
/*
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index f540f46b2a..b140e0b319 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -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);
-
- newclauses = lappend(newclauses,
- pull_up_sublinks_qual_recurse(root,
- oldclause,
- available_rels,
- fromlist));
+ Node *newclause;
+
+ 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
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 92b936a4c4..cbb78128f4 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -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 */
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index d5eee9cd2d..4e08912344 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -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)
{
/*
diff --git a/src/backend/optimizer/util/var.c b/src/backend/optimizer/util/var.c
index 1f9a035cea..38492d7b88 100644
--- a/src/backend/optimizer/util/var.c
+++ b/src/backend/optimizer/util/var.c
@@ -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 */
diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c
index 7c618dc0bd..007029ab39 100644
--- a/src/backend/rewrite/rewriteManip.c
+++ b/src/backend/rewrite/rewriteManip.c
@@ -348,23 +348,10 @@ OffsetVarNodes_walker(Node *node, OffsetVarNodes_context *context)
{
JoinExpr *j = (JoinExpr *) node;
- if (context->sublevels_up == 0)
+ if (j->rtindex && context->sublevels_up == 0)
j->rtindex += context->offset;
/* fall through to examine children */
}
- if (IsA(node, FlattenedSubLink))
- {
- FlattenedSubLink *fslink = (FlattenedSubLink *) node;
-
- if (context->sublevels_up == 0)
- {
- fslink->lefthand = offset_relid_set(fslink->lefthand,
- context->offset);
- fslink->righthand = offset_relid_set(fslink->righthand,
- context->offset);
- }
- /* fall through to examine children */
- }
if (IsA(node, PlaceHolderVar))
{
PlaceHolderVar *phv = (PlaceHolderVar *) node;
@@ -530,21 +517,6 @@ ChangeVarNodes_walker(Node *node, ChangeVarNodes_context *context)
j->rtindex = context->new_index;
/* fall through to examine children */
}
- if (IsA(node, FlattenedSubLink))
- {
- FlattenedSubLink *fslink = (FlattenedSubLink *) node;
-
- if (context->sublevels_up == 0)
- {
- fslink->lefthand = adjust_relid_set(fslink->lefthand,
- context->rt_index,
- context->new_index);
- fslink->righthand = adjust_relid_set(fslink->righthand,
- context->rt_index,
- context->new_index);
- }
- /* fall through to examine children */
- }
if (IsA(node, PlaceHolderVar))
{
PlaceHolderVar *phv = (PlaceHolderVar *) node;
@@ -838,7 +810,6 @@ rangeTableEntry_used_walker(Node *node,
/* fall through to examine children */
}
/* Shouldn't need to handle planner auxiliary nodes here */
- Assert(!IsA(node, FlattenedSubLink));
Assert(!IsA(node, PlaceHolderVar));
Assert(!IsA(node, SpecialJoinInfo));
Assert(!IsA(node, AppendRelInfo));
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 34f7cc1c32..2620a80407 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -216,7 +216,6 @@ typedef enum NodeTag
T_PathKey,
T_RestrictInfo,
T_InnerIndexscanInfo,
- T_FlattenedSubLink,
T_PlaceHolderVar,
T_SpecialJoinInfo,
T_AppendRelInfo,
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 2968d0d4cd..b279591d2b 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -1137,7 +1137,9 @@ typedef struct RangeTblRef
*
* During parse analysis, an RTE is created for the Join, and its index
* is filled into rtindex. This RTE is present mainly so that Vars can
- * be created that refer to the outputs of the join.
+ * be created that refer to the outputs of the join. The planner sometimes
+ * generates JoinExprs internally; these can have rtindex = 0 if there are
+ * no join alias variables referencing such joins.
*----------
*/
typedef struct JoinExpr
@@ -1150,7 +1152,7 @@ typedef struct JoinExpr
List *using; /* USING clause, if any (list of String) */
Node *quals; /* qualifiers on join, if any */
Alias *alias; /* user-written alias clause, if any */
- int rtindex; /* RT index assigned for join */
+ int rtindex; /* RT index assigned for join, or 0 */
} JoinExpr;
/*----------
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 443a30c3d9..06ce02946a 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -1076,30 +1076,6 @@ typedef struct InnerIndexscanInfo
} InnerIndexscanInfo;
/*
- * "Flattened SubLinks"
- *
- * When we pull an IN or EXISTS SubLink up into the parent query, the
- * join conditions extracted from the IN/EXISTS clause need to be specially
- * treated in distribute_qual_to_rels processing. We handle this by
- * wrapping such expressions in a FlattenedSubLink node that identifies
- * the join they come from. The FlattenedSubLink node is discarded after
- * distribute_qual_to_rels, having served its purpose.
- *
- * Although the planner treats this as an expression node type, it is not
- * recognized by the parser or executor, so we declare it here rather than
- * in primnodes.h.
- */
-
-typedef struct FlattenedSubLink
-{
- Expr xpr;
- JoinType jointype; /* must be JOIN_SEMI or JOIN_ANTI */
- Relids lefthand; /* base relids treated as syntactic LHS */
- Relids righthand; /* base relids syntactically within RHS */
- Expr *quals; /* join quals (in explicit-AND format) */
-} FlattenedSubLink;
-
-/*
* Placeholder node for an expression to be evaluated below the top level
* of a plan tree. This is used during planning to represent the contained
* expression. At the end of the planning process it is replaced by either
@@ -1171,8 +1147,11 @@ typedef struct PlaceHolderVar
* For purposes of join selectivity estimation, we create transient
* SpecialJoinInfo structures for regular inner joins; so it is possible
* to have jointype == JOIN_INNER in such a structure, even though this is
- * not allowed within join_info_list. Note that lhs_strict, delay_upper_joins,
- * and join_quals are not set meaningfully for such structs.
+ * not allowed within join_info_list. We also create transient
+ * SpecialJoinInfos with jointype == JOIN_INNER for outer joins, since for
+ * cost estimation purposes it is sometimes useful to know the join size under
+ * plain innerjoin semantics. Note that lhs_strict, delay_upper_joins, and
+ * join_quals are not set meaningfully within such structs.
*/
typedef struct SpecialJoinInfo
diff --git a/src/include/optimizer/subselect.h b/src/include/optimizer/subselect.h
index 5b07ef35c9..461a9a728a 100644
--- a/src/include/optimizer/subselect.h
+++ b/src/include/optimizer/subselect.h
@@ -16,13 +16,13 @@
#include "nodes/relation.h"
extern void SS_process_ctes(PlannerInfo *root);
-extern bool convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink,
- Relids available_rels,
- 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 JoinExpr *convert_ANY_sublink_to_join(PlannerInfo *root,
+ SubLink *sublink,
+ Relids available_rels);
+extern JoinExpr *convert_EXISTS_sublink_to_join(PlannerInfo *root,
+ SubLink *sublink,
+ bool under_not,
+ Relids available_rels);
extern Node *SS_replace_correlation_vars(PlannerInfo *root, Node *expr);
extern Node *SS_process_sublinks(PlannerInfo *root, Node *expr, bool isQual);
extern void SS_finalize_plan(PlannerInfo *root, Plan *plan,
diff --git a/src/test/regress/expected/join.out b/src/test/regress/expected/join.out
index 12b54aad17..8e6fc13bb3 100644
--- a/src/test/regress/expected/join.out
+++ b/src/test/regress/expected/join.out
@@ -2333,3 +2333,22 @@ where a.unique1 = 42 and
---------+-----+----------+---------+---------
(0 rows)
+--
+-- test proper positioning of one-time quals in EXISTS (8.4devel bug)
+--
+prepare foo(bool) as
+ select count(*) from tenk1 a left join tenk1 b
+ on (a.unique2 = b.unique1 and exists
+ (select 1 from tenk1 c where c.thousand = b.unique2 and $1));
+execute foo(true);
+ count
+-------
+ 10000
+(1 row)
+
+execute foo(false);
+ count
+-------
+ 10000
+(1 row)
+
diff --git a/src/test/regress/expected/join_1.out b/src/test/regress/expected/join_1.out
index 2721c3f1cf..3032beacd4 100644
--- a/src/test/regress/expected/join_1.out
+++ b/src/test/regress/expected/join_1.out
@@ -2333,3 +2333,22 @@ where a.unique1 = 42 and
---------+-----+----------+---------+---------
(0 rows)
+--
+-- test proper positioning of one-time quals in EXISTS (8.4devel bug)
+--
+prepare foo(bool) as
+ select count(*) from tenk1 a left join tenk1 b
+ on (a.unique2 = b.unique1 and exists
+ (select 1 from tenk1 c where c.thousand = b.unique2 and $1));
+execute foo(true);
+ count
+-------
+ 10000
+(1 row)
+
+execute foo(false);
+ count
+-------
+ 10000
+(1 row)
+
diff --git a/src/test/regress/sql/join.sql b/src/test/regress/sql/join.sql
index 245fde58ae..149530e14d 100644
--- a/src/test/regress/sql/join.sql
+++ b/src/test/regress/sql/join.sql
@@ -495,3 +495,13 @@ select a.unique2, a.ten, b.tenthous, b.unique2, b.hundred
from tenk1 a left join tenk1 b on a.unique2 = b.tenthous
where a.unique1 = 42 and
((b.unique2 is null and a.ten = 2) or b.hundred = 3);
+
+--
+-- test proper positioning of one-time quals in EXISTS (8.4devel bug)
+--
+prepare foo(bool) as
+ select count(*) from tenk1 a left join tenk1 b
+ on (a.unique2 = b.unique1 and exists
+ (select 1 from tenk1 c where c.thousand = b.unique2 and $1));
+execute foo(true);
+execute foo(false);