Commit e549722a authored by Tom Lane's avatar Tom Lane

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.
parent 7380b638
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.424 2009/02/24 10:06:32 petere Exp $ * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.425 2009/02/25 03:30:37 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1624,22 +1624,6 @@ _copyRestrictInfo(RestrictInfo *from) ...@@ -1624,22 +1624,6 @@ _copyRestrictInfo(RestrictInfo *from)
return newnode; return newnode;
} }
/*
* _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 * _copyPlaceHolderVar
*/ */
...@@ -3710,9 +3694,6 @@ copyObject(void *from) ...@@ -3710,9 +3694,6 @@ copyObject(void *from)
case T_RestrictInfo: case T_RestrictInfo:
retval = _copyRestrictInfo(from); retval = _copyRestrictInfo(from);
break; break;
case T_FlattenedSubLink:
retval = _copyFlattenedSubLink(from);
break;
case T_PlaceHolderVar: case T_PlaceHolderVar:
retval = _copyPlaceHolderVar(from); retval = _copyPlaceHolderVar(from);
break; break;
......
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.348 2009/02/24 10:06:32 petere Exp $ * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.349 2009/02/25 03:30:37 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -766,17 +766,6 @@ _equalRestrictInfo(RestrictInfo *a, RestrictInfo *b) ...@@ -766,17 +766,6 @@ _equalRestrictInfo(RestrictInfo *a, RestrictInfo *b)
return true; return true;
} }
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 static bool
_equalPlaceHolderVar(PlaceHolderVar *a, PlaceHolderVar *b) _equalPlaceHolderVar(PlaceHolderVar *a, PlaceHolderVar *b)
{ {
...@@ -2496,9 +2485,6 @@ equal(void *a, void *b) ...@@ -2496,9 +2485,6 @@ equal(void *a, void *b)
case T_RestrictInfo: case T_RestrictInfo:
retval = _equalRestrictInfo(a, b); retval = _equalRestrictInfo(a, b);
break; break;
case T_FlattenedSubLink:
retval = _equalFlattenedSubLink(a, b);
break;
case T_PlaceHolderVar: case T_PlaceHolderVar:
retval = _equalPlaceHolderVar(a, b); retval = _equalPlaceHolderVar(a, b);
break; break;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/nodeFuncs.c,v 1.37 2009/01/01 17:23:43 momjian Exp $ * $PostgreSQL: pgsql/src/backend/nodes/nodeFuncs.c,v 1.38 2009/02/25 03:30:37 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1309,14 +1309,6 @@ expression_tree_walker(Node *node, ...@@ -1309,14 +1309,6 @@ expression_tree_walker(Node *node,
/* groupClauses are deemed uninteresting */ /* groupClauses are deemed uninteresting */
} }
break; break;
case T_FlattenedSubLink:
{
FlattenedSubLink *fslink = (FlattenedSubLink *) node;
if (walker(fslink->quals, context))
return true;
}
break;
case T_PlaceHolderVar: case T_PlaceHolderVar:
return walker(((PlaceHolderVar *) node)->phexpr, context); return walker(((PlaceHolderVar *) node)->phexpr, context);
case T_AppendRelInfo: case T_AppendRelInfo:
...@@ -1972,17 +1964,6 @@ expression_tree_mutator(Node *node, ...@@ -1972,17 +1964,6 @@ expression_tree_mutator(Node *node,
return (Node *) newnode; return (Node *) newnode;
} }
break; 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: case T_PlaceHolderVar:
{ {
PlaceHolderVar *phv = (PlaceHolderVar *) node; PlaceHolderVar *phv = (PlaceHolderVar *) node;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.352 2009/02/06 23:43:23 tgl Exp $ * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.353 2009/02/25 03:30:37 tgl Exp $
* *
* NOTES * NOTES
* Every node type that can appear in stored rules' parsetrees *must* * Every node type that can appear in stored rules' parsetrees *must*
...@@ -1630,17 +1630,6 @@ _outInnerIndexscanInfo(StringInfo str, InnerIndexscanInfo *node) ...@@ -1630,17 +1630,6 @@ _outInnerIndexscanInfo(StringInfo str, InnerIndexscanInfo *node)
WRITE_NODE_FIELD(cheapest_total_innerpath); WRITE_NODE_FIELD(cheapest_total_innerpath);
} }
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 static void
_outPlaceHolderVar(StringInfo str, PlaceHolderVar *node) _outPlaceHolderVar(StringInfo str, PlaceHolderVar *node)
{ {
...@@ -2660,9 +2649,6 @@ _outNode(StringInfo str, void *obj) ...@@ -2660,9 +2649,6 @@ _outNode(StringInfo str, void *obj)
case T_InnerIndexscanInfo: case T_InnerIndexscanInfo:
_outInnerIndexscanInfo(str, obj); _outInnerIndexscanInfo(str, obj);
break; break;
case T_FlattenedSubLink:
_outFlattenedSubLink(str, obj);
break;
case T_PlaceHolderVar: case T_PlaceHolderVar:
_outPlaceHolderVar(str, obj); _outPlaceHolderVar(str, obj);
break; break;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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, ...@@ -52,9 +52,6 @@ static void distribute_qual_to_rels(PlannerInfo *root, Node *clause,
Relids qualscope, Relids qualscope,
Relids ojscope, Relids ojscope,
Relids outerjoin_nonnullable); 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, static bool check_outerjoin_delay(PlannerInfo *root, Relids *relids_p,
bool is_pushed_down); bool is_pushed_down);
static bool check_redundant_nullability_qual(PlannerInfo *root, Node *clause); static bool check_redundant_nullability_qual(PlannerInfo *root, Node *clause);
...@@ -336,15 +333,9 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join, ...@@ -336,15 +333,9 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
{ {
Node *qual = (Node *) lfirst(l); Node *qual = (Node *) lfirst(l);
/* FlattenedSubLink wrappers need special processing */ distribute_qual_to_rels(root, qual,
if (qual && IsA(qual, FlattenedSubLink)) false, below_outer_join, JOIN_INNER,
distribute_sublink_quals_to_rels(root, *qualscope, NULL, NULL);
(FlattenedSubLink *) qual,
below_outer_join);
else
distribute_qual_to_rels(root, qual,
false, below_outer_join, JOIN_INNER,
*qualscope, NULL, NULL);
} }
} }
else if (IsA(jtnode, JoinExpr)) else if (IsA(jtnode, JoinExpr))
...@@ -399,6 +390,18 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join, ...@@ -399,6 +390,18 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
*inner_join_rels = bms_union(left_inners, right_inners); *inner_join_rels = bms_union(left_inners, right_inners);
nonnullable_rels = leftids; nonnullable_rels = leftids;
break; 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: case JOIN_FULL:
leftjoinlist = deconstruct_recurse(root, j->larg, leftjoinlist = deconstruct_recurse(root, j->larg,
true, true,
...@@ -425,6 +428,9 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join, ...@@ -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 * 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 * 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. * 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) if (j->jointype != JOIN_INNER)
{ {
...@@ -433,7 +439,11 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join, ...@@ -433,7 +439,11 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
*inner_join_rels, *inner_join_rels,
j->jointype, j->jointype,
(List *) j->quals); (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 else
{ {
...@@ -446,16 +456,10 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join, ...@@ -446,16 +456,10 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
{ {
Node *qual = (Node *) lfirst(l); Node *qual = (Node *) lfirst(l);
/* FlattenedSubLink wrappers need special processing */ distribute_qual_to_rels(root, qual,
if (qual && IsA(qual, FlattenedSubLink)) false, below_outer_join, j->jointype,
distribute_sublink_quals_to_rels(root, *qualscope,
(FlattenedSubLink *) qual, ojscope, nonnullable_rels);
below_outer_join);
else
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 */ /* Now we can add the SpecialJoinInfo to join_info_list */
...@@ -1044,64 +1048,6 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause, ...@@ -1044,64 +1048,6 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
distribute_restrictinfo_to_rels(root, restrictinfo); 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 * check_outerjoin_delay
* Detect whether a qual referencing the given relids must be delayed * Detect whether a qual referencing the given relids must be delayed
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.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) ...@@ -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 * 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. * qual clauses, but has not checked the properties of the SubLink further.
* Decide whether it is appropriate to process this SubLink in join style. * 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 * 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. * of query rels that can safely be referenced in the sublink expression.
...@@ -945,30 +946,33 @@ SS_process_ctes(PlannerInfo *root) ...@@ -945,30 +946,33 @@ SS_process_ctes(PlannerInfo *root)
* is present in an outer join's ON qual.) The conversion must fail if * 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. * the converted qual would reference any but these parent-query relids.
* *
* On success, two output parameters are returned: * On success, the returned JoinExpr has larg = NULL and rarg = the jointree
* *new_qual is set to the qual tree that should replace the SubLink in * item representing the pulled-up subquery. The caller must set larg to
* the parent query's qual tree. The qual clauses are wrapped in a * represent the relation(s) on the lefthand side of the new join, and insert
* FlattenedSubLink node to help later processing place them properly. * the JoinExpr into the upper query's jointree at an appropriate place
* *fromlist is set to a list of pulled-up jointree item(s) that must be * (typically, where the lefthand relation(s) had been). Note that the
* added at the proper spot in the parent query's jointree. * 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 * 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, convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink,
Relids available_rels, Relids available_rels)
Node **new_qual, List **fromlist)
{ {
JoinExpr *result;
Query *parse = root->parse; Query *parse = root->parse;
Query *subselect = (Query *) sublink->subselect; Query *subselect = (Query *) sublink->subselect;
Relids left_varnos; Relids upper_varnos;
int rtindex; int rtindex;
RangeTblEntry *rte; RangeTblEntry *rte;
RangeTblRef *rtr; RangeTblRef *rtr;
List *subquery_vars; List *subquery_vars;
Expr *quals; Node *quals;
FlattenedSubLink *fslink;
Assert(sublink->subLinkType == ANY_SUBLINK); Assert(sublink->subLinkType == ANY_SUBLINK);
...@@ -977,28 +981,28 @@ convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink, ...@@ -977,28 +981,28 @@ convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink,
* higher levels should be okay, though.) * higher levels should be okay, though.)
*/ */
if (contain_vars_of_level((Node *) subselect, 1)) if (contain_vars_of_level((Node *) subselect, 1))
return 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 * else it's not gonna be a join. (Note that it won't have Vars
* referring to the subquery, rather Params.) * referring to the subquery, rather Params.)
*/ */
left_varnos = pull_varnos(sublink->testexpr); upper_varnos = pull_varnos(sublink->testexpr);
if (bms_is_empty(left_varnos)) if (bms_is_empty(upper_varnos))
return false; return NULL;
/* /*
* However, it can't refer to anything outside available_rels. * However, it can't refer to anything outside available_rels.
*/ */
if (!bms_is_subset(left_varnos, available_rels)) if (!bms_is_subset(upper_varnos, available_rels))
return false; return NULL;
/* /*
* The combining operators and left-hand expressions mustn't be volatile. * The combining operators and left-hand expressions mustn't be volatile.
*/ */
if (contain_volatile_functions(sublink->testexpr)) if (contain_volatile_functions(sublink->testexpr))
return false; return NULL;
/* /*
* Okay, pull up the sub-select into upper range table. * Okay, pull up the sub-select into upper range table.
...@@ -1016,13 +1020,10 @@ convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink, ...@@ -1016,13 +1020,10 @@ convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink,
rtindex = list_length(parse->rtable); rtindex = list_length(parse->rtable);
/* /*
* Form a RangeTblRef for the pulled-up sub-select. This must be added * Form a RangeTblRef for the pulled-up sub-select.
* to the upper jointree, but it is caller's responsibility to figure
* out where.
*/ */
rtr = makeNode(RangeTblRef); rtr = makeNode(RangeTblRef);
rtr->rtindex = rtindex; rtr->rtindex = rtindex;
*fromlist = list_make1(rtr);
/* /*
* Build a list of Vars representing the subselect outputs. * Build a list of Vars representing the subselect outputs.
...@@ -1032,53 +1033,45 @@ convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink, ...@@ -1032,53 +1033,45 @@ convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink,
rtindex); 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, quals = convert_testexpr(root, sublink->testexpr, subquery_vars);
sublink->testexpr,
subquery_vars);
/* /*
* And finally, build the FlattenedSubLink node. * And finally, build the JoinExpr 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.
*/ */
fslink = makeNode(FlattenedSubLink); result = makeNode(JoinExpr);
fslink->jointype = JOIN_SEMI; result->jointype = JOIN_SEMI;
fslink->lefthand = left_varnos; result->isNatural = false;
fslink->righthand = bms_make_singleton(rtindex); result->larg = NULL; /* caller must fill this in */
fslink->quals = quals; 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 result;
return true;
} }
/* /*
* 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, * 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, * except that we also support the case where the caller has found NOT EXISTS,
* so we need an additional input parameter "under_not". * so we need an additional input parameter "under_not".
*/ */
bool JoinExpr *
convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink,
bool under_not, bool under_not, Relids available_rels)
Relids available_rels,
Node **new_qual, List **fromlist)
{ {
JoinExpr *result;
Query *parse = root->parse; Query *parse = root->parse;
Query *subselect = (Query *) sublink->subselect; Query *subselect = (Query *) sublink->subselect;
Node *whereClause; Node *whereClause;
int rtoffset; int rtoffset;
int varno; int varno;
Relids clause_varnos; Relids clause_varnos;
Relids left_varnos; Relids upper_varnos;
Relids right_varnos;
Relids subselect_varnos;
FlattenedSubLink *fslink;
Assert(sublink->subLinkType == EXISTS_SUBLINK); Assert(sublink->subLinkType == EXISTS_SUBLINK);
...@@ -1095,13 +1088,13 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, ...@@ -1095,13 +1088,13 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink,
* us with noplace to evaluate the targetlist. * us with noplace to evaluate the targetlist.
*/ */
if (!simplify_EXISTS_query(subselect)) if (!simplify_EXISTS_query(subselect))
return false; return NULL;
/* /*
* The subquery must have a nonempty jointree, else we won't have a join. * The subquery must have a nonempty jointree, else we won't have a join.
*/ */
if (subselect->jointree->fromlist == NIL) if (subselect->jointree->fromlist == NIL)
return false; return NULL;
/* /*
* Separate out the WHERE clause. (We could theoretically also remove * Separate out the WHERE clause. (We could theoretically also remove
...@@ -1116,20 +1109,20 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, ...@@ -1116,20 +1109,20 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink,
* query. (Vars of higher levels should be okay, though.) * query. (Vars of higher levels should be okay, though.)
*/ */
if (contain_vars_of_level((Node *) subselect, 1)) if (contain_vars_of_level((Node *) subselect, 1))
return false; return NULL;
/* /*
* On the other hand, the WHERE clause must contain some Vars of the * On the other hand, the WHERE clause must contain some Vars of the
* parent query, else it's not gonna be a join. * parent query, else it's not gonna be a join.
*/ */
if (!contain_vars_of_level(whereClause, 1)) if (!contain_vars_of_level(whereClause, 1))
return false; return NULL;
/* /*
* We don't risk optimizing if the WHERE clause is volatile, either. * We don't risk optimizing if the WHERE clause is volatile, either.
*/ */
if (contain_volatile_functions(whereClause)) if (contain_volatile_functions(whereClause))
return false; return NULL;
/* /*
* Prepare to pull up the sub-select into top range table. * Prepare to pull up the sub-select into top range table.
...@@ -1142,7 +1135,7 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, ...@@ -1142,7 +1135,7 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink,
* to do. The machinations of simplify_EXISTS_query ensured that there * to do. The machinations of simplify_EXISTS_query ensured that there
* is nothing interesting in the subquery except an rtable and jointree, * is nothing interesting in the subquery except an rtable and jointree,
* and even the jointree FromExpr no longer has quals. So we can just * and even the jointree FromExpr no longer has quals. So we can just
* append the rtable to our own and 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 * But first, adjust all level-zero varnos in the subquery to account
* for the rtable merger. * for the rtable merger.
*/ */
...@@ -1161,58 +1154,47 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, ...@@ -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 * Now that the WHERE clause is adjusted to match the parent query
* environment, we can easily identify all the level-zero rels it uses. * environment, we can easily identify all the level-zero rels it uses.
* The ones <= rtoffset are "left rels" of the join we're forming, * The ones <= rtoffset belong to the upper query; the ones > rtoffset
* and the ones > rtoffset are "right rels". * do not.
*/ */
clause_varnos = pull_varnos(whereClause); clause_varnos = pull_varnos(whereClause);
left_varnos = right_varnos = NULL; upper_varnos = NULL;
while ((varno = bms_first_member(clause_varnos)) >= 0) while ((varno = bms_first_member(clause_varnos)) >= 0)
{ {
if (varno <= rtoffset) if (varno <= rtoffset)
left_varnos = bms_add_member(left_varnos, varno); upper_varnos = bms_add_member(upper_varnos, varno);
else
right_varnos = bms_add_member(right_varnos, varno);
} }
bms_free(clause_varnos); 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 * Now that we've got the set of upper-level varnos, we can make the
* last check: only available_rels can be referenced. * last check: only available_rels can be referenced.
*/ */
if (!bms_is_subset(left_varnos, available_rels)) if (!bms_is_subset(upper_varnos, available_rels))
return false; return NULL;
/* 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));
/* Now we can attach the modified subquery rtable to the parent */ /* Now we can attach the modified subquery rtable to the parent */
parse->rtable = list_concat(parse->rtable, subselect->rtable); parse->rtable = list_concat(parse->rtable, subselect->rtable);
/* /*
* Pass back the subquery fromlist to be attached to upper jointree * And finally, build the JoinExpr node.
* in a suitable place.
*/ */
*fromlist = subselect->jointree->fromlist; result = makeNode(JoinExpr);
result->jointype = under_not ? JOIN_ANTI : JOIN_SEMI;
/* result->isNatural = false;
* And finally, build the FlattenedSubLink node. result->larg = NULL; /* caller must fill this in */
* /* flatten out the FromExpr node if it's useless */
* Note: at this point left_varnos and subselect_varnos may well contain if (list_length(subselect->jointree->fromlist) == 1)
* join relids. This will get fixed when flatten_join_alias_vars is run. result->rarg = (Node *) linitial(subselect->jointree->fromlist);
*/ else
fslink = makeNode(FlattenedSubLink); result->rarg = (Node *) subselect->jointree;
fslink->jointype = under_not ? JOIN_ANTI : JOIN_SEMI; result->using = NIL;
fslink->lefthand = left_varnos; result->quals = whereClause;
fslink->righthand = subselect_varnos; result->alias = NULL;
fslink->quals = (Expr *) whereClause; result->rtindex = 0; /* we don't need an RTE for it */
*new_qual = (Node *) fslink;
return true; return result;
} }
/* /*
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
* *
* *
* IDENTIFICATION * 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 ...@@ -44,7 +44,7 @@ typedef struct reduce_outer_joins_state
static Node *pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode, static Node *pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode,
Relids *relids); Relids *relids);
static Node *pull_up_sublinks_qual_recurse(PlannerInfo *root, Node *node, 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, static Node *pull_up_simple_subquery(PlannerInfo *root, Node *jtnode,
RangeTblEntry *rte, RangeTblEntry *rte,
bool below_outer_join, bool below_outer_join,
...@@ -91,7 +91,7 @@ static Node *find_jointree_node_for_rel(Node *jtnode, int relid); ...@@ -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 * 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 * 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 * 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 * 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 * nonnullable-side variables then it would have to be evaluated as part
* of the outer join, which makes things way too complicated. * 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); ...@@ -110,13 +110,22 @@ static Node *find_jointree_node_for_rel(Node *jtnode, int relid);
void void
pull_up_sublinks(PlannerInfo *root) pull_up_sublinks(PlannerInfo *root)
{ {
Node *jtnode;
Relids relids; Relids relids;
/* Begin recursion through the jointree */ /* Begin recursion through the jointree */
root->parse->jointree = (FromExpr *) jtnode = pull_up_sublinks_jointree_recurse(root,
pull_up_sublinks_jointree_recurse(root, (Node *) root->parse->jointree,
(Node *) root->parse->jointree, &relids);
&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, ...@@ -144,9 +153,9 @@ pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode,
{ {
FromExpr *f = (FromExpr *) jtnode; FromExpr *f = (FromExpr *) jtnode;
List *newfromlist = NIL; List *newfromlist = NIL;
Node *newquals;
List *subfromlist = NIL;
Relids frelids = NULL; Relids frelids = NULL;
FromExpr *newf;
Node *jtlink;
ListCell *l; ListCell *l;
/* First, recurse to process children and collect their relids */ /* First, recurse to process children and collect their relids */
...@@ -161,26 +170,32 @@ pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode, ...@@ -161,26 +170,32 @@ pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode,
newfromlist = lappend(newfromlist, newchild); newfromlist = lappend(newfromlist, newchild);
frelids = bms_join(frelids, childrelids); 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 */ /* Now process qual --- all children are available for use */
newquals = pull_up_sublinks_qual_recurse(root, f->quals, frelids, newf->quals = pull_up_sublinks_qual_recurse(root, f->quals, frelids,
&subfromlist); &jtlink);
/* Any pulled-up subqueries can just be attached to the fromlist */
newfromlist = list_concat(newfromlist, subfromlist);
/* /*
* 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 * Although we could include the pulled-up subqueries in the returned
* relids, there's no need since upper quals couldn't refer to their * relids, there's no need since upper quals couldn't refer to their
* outputs anyway. * outputs anyway.
*/ */
*relids = frelids; *relids = frelids;
jtnode = (Node *) makeFromExpr(newfromlist, newquals); jtnode = jtlink;
} }
else if (IsA(jtnode, JoinExpr)) else if (IsA(jtnode, JoinExpr))
{ {
JoinExpr *j; JoinExpr *j;
Relids leftrelids; Relids leftrelids;
Relids rightrelids; Relids rightrelids;
List *subfromlist = NIL; Node *jtlink;
/* /*
* Make a modifiable copy of join node, but don't bother copying * 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, ...@@ -188,6 +203,7 @@ pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode,
*/ */
j = (JoinExpr *) palloc(sizeof(JoinExpr)); j = (JoinExpr *) palloc(sizeof(JoinExpr));
memcpy(j, jtnode, sizeof(JoinExpr)); memcpy(j, jtnode, sizeof(JoinExpr));
jtlink = (Node *) j;
/* Recurse to process children and collect their relids */ /* Recurse to process children and collect their relids */
j->larg = pull_up_sublinks_jointree_recurse(root, j->larg, j->larg = pull_up_sublinks_jointree_recurse(root, j->larg,
...@@ -197,13 +213,15 @@ pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode, ...@@ -197,13 +213,15 @@ pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode,
/* /*
* Now process qual, showing appropriate child relids as available, * Now process qual, showing appropriate child relids as available,
* and then attach any pulled-up jointree items at the right place. * and attach any pulled-up jointree items at the right place.
* The pulled-up items must go below where the quals that refer to * In the inner-join case we put new JoinExprs above the existing one
* them will be placed. Since the JoinExpr itself can only handle * (much as for a FromExpr-style join). In outer-join cases the
* two child nodes, we hack up a valid jointree by inserting dummy * new JoinExprs must go into the nullable side of the outer join.
* FromExprs that have no quals. These should get flattened out * The point of the available_rels machinations is to ensure that we
* during deconstruct_recurse(), so they won't impose any extra * only pull up quals for which that's okay.
* overhead. *
* We don't expect to see any pre-existing JOIN_SEMI or JOIN_ANTI
* nodes here.
*/ */
switch (j->jointype) switch (j->jointype)
{ {
...@@ -211,22 +229,12 @@ pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode, ...@@ -211,22 +229,12 @@ pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode,
j->quals = pull_up_sublinks_qual_recurse(root, j->quals, j->quals = pull_up_sublinks_qual_recurse(root, j->quals,
bms_union(leftrelids, bms_union(leftrelids,
rightrelids), rightrelids),
&subfromlist); &jtlink);
/* We arbitrarily put pulled-up subqueries into right child */
if (subfromlist)
j->rarg = (Node *) makeFromExpr(lcons(j->rarg,
subfromlist),
NULL);
break; break;
case JOIN_LEFT: case JOIN_LEFT:
j->quals = pull_up_sublinks_qual_recurse(root, j->quals, j->quals = pull_up_sublinks_qual_recurse(root, j->quals,
rightrelids, rightrelids,
&subfromlist); &j->rarg);
/* Any pulled-up subqueries must go into right child */
if (subfromlist)
j->rarg = (Node *) makeFromExpr(lcons(j->rarg,
subfromlist),
NULL);
break; break;
case JOIN_FULL: case JOIN_FULL:
/* can't do anything with full-join quals */ /* can't do anything with full-join quals */
...@@ -234,12 +242,7 @@ pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode, ...@@ -234,12 +242,7 @@ pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode,
case JOIN_RIGHT: case JOIN_RIGHT:
j->quals = pull_up_sublinks_qual_recurse(root, j->quals, j->quals = pull_up_sublinks_qual_recurse(root, j->quals,
leftrelids, leftrelids,
&subfromlist); &j->larg);
/* Any pulled-up subqueries must go into left child */
if (subfromlist)
j->larg = (Node *) makeFromExpr(lcons(j->larg,
subfromlist),
NULL);
break; break;
default: default:
elog(ERROR, "unrecognized join type: %d", elog(ERROR, "unrecognized join type: %d",
...@@ -255,9 +258,10 @@ pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode, ...@@ -255,9 +258,10 @@ pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode,
* levels would mistakenly think they couldn't use references to this * levels would mistakenly think they couldn't use references to this
* join. * join.
*/ */
*relids = bms_add_member(bms_join(leftrelids, rightrelids), *relids = bms_join(leftrelids, rightrelids);
j->rtindex); if (j->rtindex)
jtnode = (Node *) j; *relids = bms_add_member(*relids, j->rtindex);
jtnode = jtlink;
} }
else else
elog(ERROR, "unrecognized node type: %d", elog(ERROR, "unrecognized node type: %d",
...@@ -268,40 +272,47 @@ pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode, ...@@ -268,40 +272,47 @@ pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode,
/* /*
* Recurse through top-level qual nodes for pull_up_sublinks() * Recurse through top-level qual nodes for pull_up_sublinks()
* *
* Caller must have initialized *fromlist to NIL. We append any new * jtlink points to the link in the jointree where any new JoinExprs should be
* jointree items to that list. * 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 * static Node *
pull_up_sublinks_qual_recurse(PlannerInfo *root, Node *node, pull_up_sublinks_qual_recurse(PlannerInfo *root, Node *node,
Relids available_rels, List **fromlist) Relids available_rels, Node **jtlink)
{ {
if (node == NULL) if (node == NULL)
return NULL; return NULL;
if (IsA(node, SubLink)) if (IsA(node, SubLink))
{ {
SubLink *sublink = (SubLink *) node; SubLink *sublink = (SubLink *) node;
Node *new_qual; JoinExpr *j;
List *new_fromlist;
/* Is it a convertible ANY or EXISTS clause? */ /* Is it a convertible ANY or EXISTS clause? */
if (sublink->subLinkType == ANY_SUBLINK) if (sublink->subLinkType == ANY_SUBLINK)
{ {
if (convert_ANY_sublink_to_join(root, sublink, j = convert_ANY_sublink_to_join(root, sublink,
available_rels, available_rels);
&new_qual, &new_fromlist)) if (j)
{ {
*fromlist = list_concat(*fromlist, new_fromlist); /* Yes, insert the new join node into the join tree */
return new_qual; j->larg = *jtlink;
*jtlink = (Node *) j;
/* and return NULL representing constant TRUE */
return NULL;
} }
} }
else if (sublink->subLinkType == EXISTS_SUBLINK) else if (sublink->subLinkType == EXISTS_SUBLINK)
{ {
if (convert_EXISTS_sublink_to_join(root, sublink, false, j = convert_EXISTS_sublink_to_join(root, sublink, false,
available_rels, available_rels);
&new_qual, &new_fromlist)) if (j)
{ {
*fromlist = list_concat(*fromlist, new_fromlist); /* Yes, insert the new join node into the join tree */
return new_qual; j->larg = *jtlink;
*jtlink = (Node *) j;
/* and return NULL representing constant TRUE */
return NULL;
} }
} }
/* Else return it unmodified */ /* Else return it unmodified */
...@@ -311,19 +322,21 @@ pull_up_sublinks_qual_recurse(PlannerInfo *root, Node *node, ...@@ -311,19 +322,21 @@ pull_up_sublinks_qual_recurse(PlannerInfo *root, Node *node,
{ {
/* If the immediate argument of NOT is EXISTS, try to convert */ /* If the immediate argument of NOT is EXISTS, try to convert */
SubLink *sublink = (SubLink *) get_notclausearg((Expr *) node); SubLink *sublink = (SubLink *) get_notclausearg((Expr *) node);
Node *new_qual; JoinExpr *j;
List *new_fromlist;
if (sublink && IsA(sublink, SubLink)) if (sublink && IsA(sublink, SubLink))
{ {
if (sublink->subLinkType == EXISTS_SUBLINK) if (sublink->subLinkType == EXISTS_SUBLINK)
{ {
if (convert_EXISTS_sublink_to_join(root, sublink, true, j = convert_EXISTS_sublink_to_join(root, sublink, true,
available_rels, available_rels);
&new_qual, &new_fromlist)) if (j)
{ {
*fromlist = list_concat(*fromlist, new_fromlist); /* Yes, insert the new join node into the join tree */
return new_qual; 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, ...@@ -339,14 +352,22 @@ pull_up_sublinks_qual_recurse(PlannerInfo *root, Node *node,
foreach(l, ((BoolExpr *) node)->args) foreach(l, ((BoolExpr *) node)->args)
{ {
Node *oldclause = (Node *) lfirst(l); Node *oldclause = (Node *) lfirst(l);
Node *newclause;
newclauses = lappend(newclauses,
pull_up_sublinks_qual_recurse(root, newclause = pull_up_sublinks_qual_recurse(root,
oldclause, oldclause,
available_rels, available_rels,
fromlist)); 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 */ /* Stop if not an AND */
return node; return node;
...@@ -489,6 +510,8 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode, ...@@ -489,6 +510,8 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode,
below_outer_join, false); below_outer_join, false);
break; break;
case JOIN_LEFT: case JOIN_LEFT:
case JOIN_SEMI:
case JOIN_ANTI:
j->larg = pull_up_subqueries(root, j->larg, j->larg = pull_up_subqueries(root, j->larg,
below_outer_join, false); below_outer_join, false);
j->rarg = pull_up_subqueries(root, j->rarg, j->rarg = pull_up_subqueries(root, j->rarg,
...@@ -702,12 +725,12 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, ...@@ -702,12 +725,12 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
parse->rowMarks = list_concat(parse->rowMarks, subquery->rowMarks); parse->rowMarks = list_concat(parse->rowMarks, subquery->rowMarks);
/* /*
* We also have to fix the relid sets of any FlattenedSubLink and * We also have to fix the relid sets of any PlaceHolderVar nodes in the
* PlaceHolderVar nodes in the parent query. (This could perhaps be done * parent query. (This could perhaps be done by ResolveNew, but it would
* by ResolveNew, but it would clutter that routine's API unreasonably.) * clutter that routine's API unreasonably.) Note in particular that any
* Note in particular that any PlaceHolderVar nodes just created by * PlaceHolderVar nodes just created by insert_targetlist_placeholders()
* insert_targetlist_placeholders() will be adjusted, so having created * will be adjusted, so having created them with the subquery's varno is
* them with the subquery's varno is correct. * correct.
* *
* Likewise, relids appearing in AppendRelInfo nodes have to be fixed. * Likewise, relids appearing in AppendRelInfo nodes have to be fixed.
* We already checked that this won't require introducing multiple * We already checked that this won't require introducing multiple
...@@ -1419,6 +1442,14 @@ reduce_outer_joins_pass2(Node *jtnode, ...@@ -1419,6 +1442,14 @@ reduce_outer_joins_pass2(Node *jtnode,
jointype = JOIN_RIGHT; jointype = JOIN_RIGHT;
} }
break; 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: default:
elog(ERROR, "unrecognized join type: %d", elog(ERROR, "unrecognized join type: %d",
(int) jointype); (int) jointype);
...@@ -1475,14 +1506,15 @@ reduce_outer_joins_pass2(Node *jtnode, ...@@ -1475,14 +1506,15 @@ reduce_outer_joins_pass2(Node *jtnode,
} }
/* Apply the jointype change, if any, to both jointree node and RTE */ /* 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); RangeTblEntry *rte = rt_fetch(rtindex, root->parse->rtable);
Assert(rte->rtekind == RTE_JOIN); Assert(rte->rtekind == RTE_JOIN);
Assert(rte->jointype == j->jointype); 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 */ /* Only recurse if there's more to do below here */
if (left_state->contains_outer || right_state->contains_outer) if (left_state->contains_outer || right_state->contains_outer)
...@@ -1542,7 +1574,7 @@ reduce_outer_joins_pass2(Node *jtnode, ...@@ -1542,7 +1574,7 @@ reduce_outer_joins_pass2(Node *jtnode,
pass_nonnullable_vars = local_nonnullable_vars; pass_nonnullable_vars = local_nonnullable_vars;
pass_forced_null_vars = local_forced_null_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 */ /* can't pass local constraints to non-nullable side */
pass_nonnullable_rels = nonnullable_rels; pass_nonnullable_rels = nonnullable_rels;
...@@ -1564,7 +1596,7 @@ reduce_outer_joins_pass2(Node *jtnode, ...@@ -1564,7 +1596,7 @@ reduce_outer_joins_pass2(Node *jtnode,
if (right_state->contains_outer) 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 appropriate constraints, per comment above */
pass_nonnullable_rels = local_nonnullable_rels; pass_nonnullable_rels = local_nonnullable_rels;
...@@ -1595,10 +1627,10 @@ reduce_outer_joins_pass2(Node *jtnode, ...@@ -1595,10 +1627,10 @@ reduce_outer_joins_pass2(Node *jtnode,
* substitute_multiple_relids - adjust node relid sets after pulling up * substitute_multiple_relids - adjust node relid sets after pulling up
* a subquery * a subquery
* *
* Find any FlattenedSubLink or PlaceHolderVar nodes in the given tree that * Find any PlaceHolderVar nodes in the given tree that reference the
* reference the pulled-up relid, and change them to reference the replacement * pulled-up relid, and change them to reference the replacement relid(s).
* relid(s). We do not need to recurse into subqueries, since no subquery of * We do not need to recurse into subqueries, since no subquery of the current
* the current top query could (yet) contain such a reference. * top query could (yet) contain such a reference.
* *
* NOTE: although this has the form of a walker, we cheat and modify the * 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 * nodes in-place. This should be OK since the tree was copied by ResolveNew
...@@ -1618,26 +1650,6 @@ substitute_multiple_relids_walker(Node *node, ...@@ -1618,26 +1650,6 @@ substitute_multiple_relids_walker(Node *node,
{ {
if (node == NULL) if (node == NULL)
return false; 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)) if (IsA(node, PlaceHolderVar))
{ {
PlaceHolderVar *phv = (PlaceHolderVar *) node; PlaceHolderVar *phv = (PlaceHolderVar *) node;
...@@ -1757,7 +1769,7 @@ get_relids_in_jointree(Node *jtnode, bool include_joins) ...@@ -1757,7 +1769,7 @@ get_relids_in_jointree(Node *jtnode, bool include_joins)
result = get_relids_in_jointree(j->larg, include_joins); result = get_relids_in_jointree(j->larg, include_joins);
result = bms_join(result, result = bms_join(result,
get_relids_in_jointree(j->rarg, include_joins)); get_relids_in_jointree(j->rarg, include_joins));
if (include_joins) if (include_joins && j->rtindex)
result = bms_add_member(result, j->rtindex); result = bms_add_member(result, j->rtindex);
} }
else else
......
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -1587,23 +1587,6 @@ adjust_appendrel_attrs_mutator(Node *node, AppendRelInfo *context)
j->rtindex = context->child_relid; j->rtindex = context->child_relid;
return (Node *) j; 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)) if (IsA(node, PlaceHolderVar))
{ {
/* Copy the PlaceHolderVar node with correct mutation of subnodes */ /* Copy the PlaceHolderVar node with correct mutation of subnodes */
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -1300,15 +1300,6 @@ find_nonnullable_rels_walker(Node *node, bool top_level) ...@@ -1300,15 +1300,6 @@ find_nonnullable_rels_walker(Node *node, bool top_level)
expr->booltesttype == IS_NOT_UNKNOWN)) expr->booltesttype == IS_NOT_UNKNOWN))
result = find_nonnullable_rels_walker((Node *) expr->arg, false); 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)) else if (IsA(node, PlaceHolderVar))
{ {
PlaceHolderVar *phv = (PlaceHolderVar *) node; PlaceHolderVar *phv = (PlaceHolderVar *) node;
...@@ -1511,15 +1502,6 @@ find_nonnullable_vars_walker(Node *node, bool top_level) ...@@ -1511,15 +1502,6 @@ find_nonnullable_vars_walker(Node *node, bool top_level)
expr->booltesttype == IS_NOT_UNKNOWN)) expr->booltesttype == IS_NOT_UNKNOWN))
result = find_nonnullable_vars_walker((Node *) expr->arg, false); 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)) else if (IsA(node, PlaceHolderVar))
{ {
PlaceHolderVar *phv = (PlaceHolderVar *) node; PlaceHolderVar *phv = (PlaceHolderVar *) node;
...@@ -2943,24 +2925,6 @@ eval_const_expressions_mutator(Node *node, ...@@ -2943,24 +2925,6 @@ eval_const_expressions_mutator(Node *node,
newbtest->booltesttype = btest->booltesttype; newbtest->booltesttype = btest->booltesttype;
return (Node *) newbtest; 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) if (IsA(node, PlaceHolderVar) && context->estimate)
{ {
/* /*
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
* *
* *
* IDENTIFICATION * 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, ...@@ -766,24 +766,6 @@ flatten_join_alias_vars_mutator(Node *node,
/* Recurse in case join input is itself a join */ /* Recurse in case join input is itself a join */
return flatten_join_alias_vars_mutator(newvar, context); 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)) if (IsA(node, PlaceHolderVar))
{ {
/* Copy the PlaceHolderVar node with correct mutation of subnodes */ /* Copy the PlaceHolderVar node with correct mutation of subnodes */
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/rewrite/rewriteManip.c,v 1.120 2009/01/01 17:23:47 momjian Exp $ * $PostgreSQL: pgsql/src/backend/rewrite/rewriteManip.c,v 1.121 2009/02/25 03:30:37 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -348,23 +348,10 @@ OffsetVarNodes_walker(Node *node, OffsetVarNodes_context *context) ...@@ -348,23 +348,10 @@ OffsetVarNodes_walker(Node *node, OffsetVarNodes_context *context)
{ {
JoinExpr *j = (JoinExpr *) node; JoinExpr *j = (JoinExpr *) node;
if (context->sublevels_up == 0) if (j->rtindex && context->sublevels_up == 0)
j->rtindex += context->offset; j->rtindex += context->offset;
/* fall through to examine children */ /* 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)) if (IsA(node, PlaceHolderVar))
{ {
PlaceHolderVar *phv = (PlaceHolderVar *) node; PlaceHolderVar *phv = (PlaceHolderVar *) node;
...@@ -530,21 +517,6 @@ ChangeVarNodes_walker(Node *node, ChangeVarNodes_context *context) ...@@ -530,21 +517,6 @@ ChangeVarNodes_walker(Node *node, ChangeVarNodes_context *context)
j->rtindex = context->new_index; j->rtindex = context->new_index;
/* fall through to examine children */ /* 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)) if (IsA(node, PlaceHolderVar))
{ {
PlaceHolderVar *phv = (PlaceHolderVar *) node; PlaceHolderVar *phv = (PlaceHolderVar *) node;
...@@ -838,7 +810,6 @@ rangeTableEntry_used_walker(Node *node, ...@@ -838,7 +810,6 @@ rangeTableEntry_used_walker(Node *node,
/* fall through to examine children */ /* fall through to examine children */
} }
/* Shouldn't need to handle planner auxiliary nodes here */ /* Shouldn't need to handle planner auxiliary nodes here */
Assert(!IsA(node, FlattenedSubLink));
Assert(!IsA(node, PlaceHolderVar)); Assert(!IsA(node, PlaceHolderVar));
Assert(!IsA(node, SpecialJoinInfo)); Assert(!IsA(node, SpecialJoinInfo));
Assert(!IsA(node, AppendRelInfo)); Assert(!IsA(node, AppendRelInfo));
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.220 2009/02/02 19:31:40 alvherre Exp $ * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.221 2009/02/25 03:30:37 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -216,7 +216,6 @@ typedef enum NodeTag ...@@ -216,7 +216,6 @@ typedef enum NodeTag
T_PathKey, T_PathKey,
T_RestrictInfo, T_RestrictInfo,
T_InnerIndexscanInfo, T_InnerIndexscanInfo,
T_FlattenedSubLink,
T_PlaceHolderVar, T_PlaceHolderVar,
T_SpecialJoinInfo, T_SpecialJoinInfo,
T_AppendRelInfo, T_AppendRelInfo,
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.145 2009/01/01 17:24:00 momjian Exp $ * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.146 2009/02/25 03:30:37 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1137,7 +1137,9 @@ typedef struct RangeTblRef ...@@ -1137,7 +1137,9 @@ typedef struct RangeTblRef
* *
* During parse analysis, an RTE is created for the Join, and its index * 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 * 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 typedef struct JoinExpr
...@@ -1150,7 +1152,7 @@ typedef struct JoinExpr ...@@ -1150,7 +1152,7 @@ typedef struct JoinExpr
List *using; /* USING clause, if any (list of String) */ List *using; /* USING clause, if any (list of String) */
Node *quals; /* qualifiers on join, if any */ Node *quals; /* qualifiers on join, if any */
Alias *alias; /* user-written alias clause, 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; } JoinExpr;
/*---------- /*----------
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.168 2009/02/06 23:43:24 tgl Exp $ * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.169 2009/02/25 03:30:37 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1075,30 +1075,6 @@ typedef struct InnerIndexscanInfo ...@@ -1075,30 +1075,6 @@ typedef struct InnerIndexscanInfo
Path *cheapest_total_innerpath; /* cheapest total cost */ Path *cheapest_total_innerpath; /* cheapest total cost */
} 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 * 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 * of a plan tree. This is used during planning to represent the contained
...@@ -1171,8 +1147,11 @@ typedef struct PlaceHolderVar ...@@ -1171,8 +1147,11 @@ typedef struct PlaceHolderVar
* For purposes of join selectivity estimation, we create transient * For purposes of join selectivity estimation, we create transient
* SpecialJoinInfo structures for regular inner joins; so it is possible * SpecialJoinInfo structures for regular inner joins; so it is possible
* to have jointype == JOIN_INNER in such a structure, even though this is * 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, * not allowed within join_info_list. We also create transient
* and join_quals are not set meaningfully for such structs. * 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 typedef struct SpecialJoinInfo
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/optimizer/subselect.h,v 1.35 2009/01/01 17:24:00 momjian Exp $ * $PostgreSQL: pgsql/src/include/optimizer/subselect.h,v 1.36 2009/02/25 03:30:38 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -16,13 +16,13 @@ ...@@ -16,13 +16,13 @@
#include "nodes/relation.h" #include "nodes/relation.h"
extern void SS_process_ctes(PlannerInfo *root); extern void SS_process_ctes(PlannerInfo *root);
extern bool convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink, extern JoinExpr *convert_ANY_sublink_to_join(PlannerInfo *root,
Relids available_rels, SubLink *sublink,
Node **new_qual, List **fromlist); Relids available_rels);
extern bool convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, extern JoinExpr *convert_EXISTS_sublink_to_join(PlannerInfo *root,
bool under_not, SubLink *sublink,
Relids available_rels, bool under_not,
Node **new_qual, List **fromlist); Relids available_rels);
extern Node *SS_replace_correlation_vars(PlannerInfo *root, Node *expr); extern Node *SS_replace_correlation_vars(PlannerInfo *root, Node *expr);
extern Node *SS_process_sublinks(PlannerInfo *root, Node *expr, bool isQual); extern Node *SS_process_sublinks(PlannerInfo *root, Node *expr, bool isQual);
extern void SS_finalize_plan(PlannerInfo *root, Plan *plan, extern void SS_finalize_plan(PlannerInfo *root, Plan *plan,
......
...@@ -2333,3 +2333,22 @@ where a.unique1 = 42 and ...@@ -2333,3 +2333,22 @@ where a.unique1 = 42 and
---------+-----+----------+---------+--------- ---------+-----+----------+---------+---------
(0 rows) (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)
...@@ -2333,3 +2333,22 @@ where a.unique1 = 42 and ...@@ -2333,3 +2333,22 @@ where a.unique1 = 42 and
---------+-----+----------+---------+--------- ---------+-----+----------+---------+---------
(0 rows) (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)
...@@ -495,3 +495,13 @@ select a.unique2, a.ten, b.tenthous, b.unique2, b.hundred ...@@ -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 from tenk1 a left join tenk1 b on a.unique2 = b.tenthous
where a.unique1 = 42 and where a.unique1 = 42 and
((b.unique2 is null and a.ten = 2) or b.hundred = 3); ((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);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment