diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 2175e0c0fc0..ef3d5fc3732 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -15,7 +15,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * 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) 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 */ @@ -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 4d905dc945e..6b2d99b943a 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -22,7 +22,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * 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) 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 _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 683436f17ba..8a04d71eb82 100644 --- a/src/backend/nodes/nodeFuncs.c +++ b/src/backend/nodes/nodeFuncs.c @@ -8,7 +8,7 @@ * * * 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, /* 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 5dc9db98bf9..c68dea8a2ce 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -8,7 +8,7 @@ * * * 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 * Every node type that can appear in stored rules' parsetrees *must* @@ -1630,17 +1630,6 @@ _outInnerIndexscanInfo(StringInfo str, InnerIndexscanInfo *node) 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 _outPlaceHolderVar(StringInfo str, PlaceHolderVar *node) { @@ -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 1b3296f971a..25117716c34 100644 --- a/src/backend/optimizer/plan/initsplan.c +++ b/src/backend/optimizer/plan/initsplan.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/plan/initsplan.c,v 1.147 2009/02/20 00:01:03 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/plan/initsplan.c,v 1.148 2009/02/25 03:30:37 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -52,9 +52,6 @@ static void distribute_qual_to_rels(PlannerInfo *root, Node *clause, Relids qualscope, Relids ojscope, Relids outerjoin_nonnullable); -static void distribute_sublink_quals_to_rels(PlannerInfo *root, - FlattenedSubLink *fslink, - bool below_outer_join); static bool check_outerjoin_delay(PlannerInfo *root, Relids *relids_p, bool is_pushed_down); static bool check_redundant_nullability_qual(PlannerInfo *root, Node *clause); @@ -336,15 +333,9 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join, { Node *qual = (Node *) lfirst(l); - /* FlattenedSubLink wrappers need special processing */ - if (qual && IsA(qual, FlattenedSubLink)) - distribute_sublink_quals_to_rels(root, - (FlattenedSubLink *) qual, - below_outer_join); - else - distribute_qual_to_rels(root, qual, - false, below_outer_join, JOIN_INNER, - *qualscope, NULL, NULL); + distribute_qual_to_rels(root, qual, + false, below_outer_join, JOIN_INNER, + *qualscope, NULL, NULL); } } else if (IsA(jtnode, JoinExpr)) @@ -399,6 +390,18 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join, *inner_join_rels = bms_union(left_inners, right_inners); nonnullable_rels = leftids; break; + case JOIN_SEMI: + leftjoinlist = deconstruct_recurse(root, j->larg, + below_outer_join, + &leftids, &left_inners); + rightjoinlist = deconstruct_recurse(root, j->rarg, + below_outer_join, + &rightids, &right_inners); + *qualscope = bms_union(leftids, rightids); + *inner_join_rels = bms_union(left_inners, right_inners); + /* Semi join adds no restrictions for quals */ + nonnullable_rels = NULL; + break; case JOIN_FULL: leftjoinlist = deconstruct_recurse(root, j->larg, true, @@ -425,6 +428,9 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join, * semantic scope (ojscope) to pass to distribute_qual_to_rels. But * we mustn't add it to join_info_list just yet, because we don't want * distribute_qual_to_rels to think it is an outer join below us. + * + * Semijoins are a bit of a hybrid: we build a SpecialJoinInfo, + * but we want ojscope = NULL for distribute_qual_to_rels. */ if (j->jointype != JOIN_INNER) { @@ -433,7 +439,11 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join, *inner_join_rels, j->jointype, (List *) j->quals); - ojscope = bms_union(sjinfo->min_lefthand, sjinfo->min_righthand); + if (j->jointype == JOIN_SEMI) + ojscope = NULL; + else + ojscope = bms_union(sjinfo->min_lefthand, + sjinfo->min_righthand); } else { @@ -446,16 +456,10 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join, { Node *qual = (Node *) lfirst(l); - /* FlattenedSubLink wrappers need special processing */ - if (qual && IsA(qual, FlattenedSubLink)) - distribute_sublink_quals_to_rels(root, - (FlattenedSubLink *) qual, - below_outer_join); - else - distribute_qual_to_rels(root, qual, - false, below_outer_join, j->jointype, - *qualscope, - ojscope, nonnullable_rels); + distribute_qual_to_rels(root, qual, + false, below_outer_join, j->jointype, + *qualscope, + ojscope, nonnullable_rels); } /* Now we can add the SpecialJoinInfo to join_info_list */ @@ -1044,64 +1048,6 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause, distribute_restrictinfo_to_rels(root, restrictinfo); } -/* - * distribute_sublink_quals_to_rels - * Pull sublink quals out of a FlattenedSubLink node and distribute - * them appropriately; then add a SpecialJoinInfo node to the query's - * join_info_list. The FlattenedSubLink node itself is no longer - * needed and does not propagate into further processing. - */ -static void -distribute_sublink_quals_to_rels(PlannerInfo *root, - FlattenedSubLink *fslink, - bool below_outer_join) -{ - List *quals = make_ands_implicit(fslink->quals); - SpecialJoinInfo *sjinfo; - Relids qualscope; - Relids ojscope; - Relids outerjoin_nonnullable; - ListCell *l; - - /* - * Build a suitable SpecialJoinInfo for the sublink. Note: using - * righthand as inner_join_rels is the conservative worst case; - * it might be possible to use a smaller set and thereby allow - * the sublink join to commute with others inside its RHS. - */ - sjinfo = make_outerjoininfo(root, - fslink->lefthand, fslink->righthand, - fslink->righthand, - fslink->jointype, - quals); - - /* Treat as inner join if SEMI, outer join if ANTI */ - qualscope = bms_union(sjinfo->syn_lefthand, sjinfo->syn_righthand); - if (fslink->jointype == JOIN_SEMI) - { - ojscope = outerjoin_nonnullable = NULL; - } - else - { - Assert(fslink->jointype == JOIN_ANTI); - ojscope = bms_union(sjinfo->min_lefthand, sjinfo->min_righthand); - outerjoin_nonnullable = fslink->lefthand; - } - - /* Distribute the join quals much as for a regular JOIN node */ - foreach(l, quals) - { - Node *qual = (Node *) lfirst(l); - - distribute_qual_to_rels(root, qual, - false, below_outer_join, fslink->jointype, - qualscope, ojscope, outerjoin_nonnullable); - } - - /* Now we can add the SpecialJoinInfo to join_info_list */ - root->join_info_list = lappend(root->join_info_list, sjinfo); -} - /* * check_outerjoin_delay * Detect whether a qual referencing the given relids must be delayed diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index e8484437fd1..88dcbc23049 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.145 2009/01/01 17:23:44 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.146 2009/02/25 03:30:37 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -932,12 +932,13 @@ SS_process_ctes(PlannerInfo *root) } /* - * convert_ANY_sublink_to_join: can we convert an ANY SubLink to a join? + * convert_ANY_sublink_to_join: try to convert an ANY SubLink to a join * * The caller has found an ANY SubLink at the top level of one of the query's * qual clauses, but has not checked the properties of the SubLink further. * Decide whether it is appropriate to process this SubLink in join style. - * Return TRUE if so, FALSE if the SubLink cannot be converted. + * If so, form a JoinExpr and return it. Return NULL if the SubLink cannot + * be converted to a join. * * The only non-obvious input parameter is available_rels: this is the set * of query rels that can safely be referenced in the sublink expression. @@ -945,30 +946,33 @@ SS_process_ctes(PlannerInfo *root) * is present in an outer join's ON qual.) The conversion must fail if * the converted qual would reference any but these parent-query relids. * - * On success, two output parameters are returned: - * *new_qual is set to the qual tree that should replace the SubLink in - * the parent query's qual tree. The qual clauses are wrapped in a - * FlattenedSubLink node to help later processing place them properly. - * *fromlist is set to a list of pulled-up jointree item(s) that must be - * added at the proper spot in the parent query's jointree. + * On success, the returned JoinExpr has larg = NULL and rarg = the jointree + * item representing the pulled-up subquery. The caller must set larg to + * represent the relation(s) on the lefthand side of the new join, and insert + * the JoinExpr into the upper query's jointree at an appropriate place + * (typically, where the lefthand relation(s) had been). Note that the + * passed-in SubLink must also be removed from its original position in the + * query quals, since the quals of the returned JoinExpr replace it. + * (Notionally, we replace the SubLink with a constant TRUE, then elide the + * redundant constant from the qual.) * * Side effects of a successful conversion include adding the SubLink's - * subselect to the query's rangetable. + * subselect to the query's rangetable, so that it can be referenced in + * the JoinExpr's rarg. */ -bool +JoinExpr * convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink, - Relids available_rels, - Node **new_qual, List **fromlist) + Relids available_rels) { + JoinExpr *result; Query *parse = root->parse; Query *subselect = (Query *) sublink->subselect; - Relids left_varnos; + Relids upper_varnos; int rtindex; RangeTblEntry *rte; RangeTblRef *rtr; List *subquery_vars; - Expr *quals; - FlattenedSubLink *fslink; + Node *quals; Assert(sublink->subLinkType == ANY_SUBLINK); @@ -977,28 +981,28 @@ convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink, * higher levels should be okay, though.) */ if (contain_vars_of_level((Node *) subselect, 1)) - return false; + return NULL; /* - * The test expression must contain some Vars of the current query, + * The test expression must contain some Vars of the parent query, * else it's not gonna be a join. (Note that it won't have Vars * referring to the subquery, rather Params.) */ - left_varnos = pull_varnos(sublink->testexpr); - if (bms_is_empty(left_varnos)) - return false; + upper_varnos = pull_varnos(sublink->testexpr); + if (bms_is_empty(upper_varnos)) + return NULL; /* * However, it can't refer to anything outside available_rels. */ - if (!bms_is_subset(left_varnos, available_rels)) - return false; + if (!bms_is_subset(upper_varnos, available_rels)) + return NULL; /* * The combining operators and left-hand expressions mustn't be volatile. */ if (contain_volatile_functions(sublink->testexpr)) - return false; + return NULL; /* * Okay, pull up the sub-select into upper range table. @@ -1016,13 +1020,10 @@ convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink, rtindex = list_length(parse->rtable); /* - * Form a RangeTblRef for the pulled-up sub-select. This must be added - * to the upper jointree, but it is caller's responsibility to figure - * out where. + * Form a RangeTblRef for the pulled-up sub-select. */ rtr = makeNode(RangeTblRef); rtr->rtindex = rtindex; - *fromlist = list_make1(rtr); /* * Build a list of Vars representing the subselect outputs. @@ -1032,53 +1033,45 @@ convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink, rtindex); /* - * Build the replacement qual expression, replacing Params with these Vars. + * Build the new join's qual expression, replacing Params with these Vars. */ - quals = (Expr *) convert_testexpr(root, - sublink->testexpr, - subquery_vars); + quals = convert_testexpr(root, sublink->testexpr, subquery_vars); /* - * And finally, build the FlattenedSubLink node. - * - * Note: at this point left_varnos may well contain join relids, since - * the testexpr hasn't been run through flatten_join_alias_vars. This - * will get fixed when flatten_join_alias_vars is run. + * And finally, build the JoinExpr node. */ - fslink = makeNode(FlattenedSubLink); - fslink->jointype = JOIN_SEMI; - fslink->lefthand = left_varnos; - fslink->righthand = bms_make_singleton(rtindex); - fslink->quals = quals; + result = makeNode(JoinExpr); + result->jointype = JOIN_SEMI; + result->isNatural = false; + result->larg = NULL; /* caller must fill this in */ + result->rarg = (Node *) rtr; + result->using = NIL; + result->quals = quals; + result->alias = NULL; + result->rtindex = 0; /* we don't need an RTE for it */ - *new_qual = (Node *) fslink; - - return true; + return result; } /* - * convert_EXISTS_sublink_to_join: can we convert an EXISTS SubLink to a join? + * convert_EXISTS_sublink_to_join: try to convert an EXISTS SubLink to a join * * The API of this function is identical to convert_ANY_sublink_to_join's, * except that we also support the case where the caller has found NOT EXISTS, * so we need an additional input parameter "under_not". */ -bool +JoinExpr * convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, - bool under_not, - Relids available_rels, - Node **new_qual, List **fromlist) + bool under_not, Relids available_rels) { + JoinExpr *result; Query *parse = root->parse; Query *subselect = (Query *) sublink->subselect; Node *whereClause; int rtoffset; int varno; Relids clause_varnos; - Relids left_varnos; - Relids right_varnos; - Relids subselect_varnos; - FlattenedSubLink *fslink; + Relids upper_varnos; Assert(sublink->subLinkType == EXISTS_SUBLINK); @@ -1095,13 +1088,13 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, * us with noplace to evaluate the targetlist. */ if (!simplify_EXISTS_query(subselect)) - return false; + return NULL; /* * The subquery must have a nonempty jointree, else we won't have a join. */ if (subselect->jointree->fromlist == NIL) - return false; + return NULL; /* * Separate out the WHERE clause. (We could theoretically also remove @@ -1116,20 +1109,20 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, * query. (Vars of higher levels should be okay, though.) */ if (contain_vars_of_level((Node *) subselect, 1)) - return false; + return NULL; /* * On the other hand, the WHERE clause must contain some Vars of the * parent query, else it's not gonna be a join. */ if (!contain_vars_of_level(whereClause, 1)) - return false; + return NULL; /* * We don't risk optimizing if the WHERE clause is volatile, either. */ if (contain_volatile_functions(whereClause)) - return false; + return NULL; /* * Prepare to pull up the sub-select into top range table. @@ -1142,7 +1135,7 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, * to do. The machinations of simplify_EXISTS_query ensured that there * is nothing interesting in the subquery except an rtable and jointree, * and even the jointree FromExpr no longer has quals. So we can just - * append the rtable to our own and attach the fromlist to our own. + * append the rtable to our own and use the FromExpr in our jointree. * But first, adjust all level-zero varnos in the subquery to account * for the rtable merger. */ @@ -1161,58 +1154,47 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, /* * Now that the WHERE clause is adjusted to match the parent query * environment, we can easily identify all the level-zero rels it uses. - * The ones <= rtoffset are "left rels" of the join we're forming, - * and the ones > rtoffset are "right rels". + * The ones <= rtoffset belong to the upper query; the ones > rtoffset + * do not. */ clause_varnos = pull_varnos(whereClause); - left_varnos = right_varnos = NULL; + upper_varnos = NULL; while ((varno = bms_first_member(clause_varnos)) >= 0) { if (varno <= rtoffset) - left_varnos = bms_add_member(left_varnos, varno); - else - right_varnos = bms_add_member(right_varnos, varno); + upper_varnos = bms_add_member(upper_varnos, varno); } bms_free(clause_varnos); - Assert(!bms_is_empty(left_varnos)); + Assert(!bms_is_empty(upper_varnos)); /* * Now that we've got the set of upper-level varnos, we can make the * last check: only available_rels can be referenced. */ - if (!bms_is_subset(left_varnos, available_rels)) - return false; - - /* Identify all the rels syntactically within the subselect */ - subselect_varnos = get_relids_in_jointree((Node *) subselect->jointree, - true); - Assert(!bms_is_empty(subselect_varnos)); - Assert(bms_is_subset(right_varnos, subselect_varnos)); + if (!bms_is_subset(upper_varnos, available_rels)) + return NULL; /* Now we can attach the modified subquery rtable to the parent */ parse->rtable = list_concat(parse->rtable, subselect->rtable); /* - * Pass back the subquery fromlist to be attached to upper jointree - * in a suitable place. + * And finally, build the JoinExpr node. */ - *fromlist = subselect->jointree->fromlist; + result = makeNode(JoinExpr); + result->jointype = under_not ? JOIN_ANTI : JOIN_SEMI; + result->isNatural = false; + result->larg = NULL; /* caller must fill this in */ + /* flatten out the FromExpr node if it's useless */ + if (list_length(subselect->jointree->fromlist) == 1) + result->rarg = (Node *) linitial(subselect->jointree->fromlist); + else + result->rarg = (Node *) subselect->jointree; + result->using = NIL; + result->quals = whereClause; + result->alias = NULL; + result->rtindex = 0; /* we don't need an RTE for it */ - /* - * And finally, build the FlattenedSubLink node. - * - * Note: at this point left_varnos and subselect_varnos may well contain - * join relids. This will get fixed when flatten_join_alias_vars is run. - */ - fslink = makeNode(FlattenedSubLink); - fslink->jointype = under_not ? JOIN_ANTI : JOIN_SEMI; - fslink->lefthand = left_varnos; - fslink->righthand = subselect_varnos; - fslink->quals = (Expr *) whereClause; - - *new_qual = (Node *) fslink; - - return true; + return result; } /* diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c index 9896b102806..600facb578d 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -16,7 +16,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.62 2009/01/01 17:23:44 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.63 2009/02/25 03:30:37 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -44,7 +44,7 @@ typedef struct reduce_outer_joins_state static Node *pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode, Relids *relids); static Node *pull_up_sublinks_qual_recurse(PlannerInfo *root, Node *node, - Relids available_rels, List **fromlist); + Relids available_rels, Node **jtlink); static Node *pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, bool below_outer_join, @@ -91,7 +91,7 @@ static Node *find_jointree_node_for_rel(Node *jtnode, int relid); * distinguish whether the ANY ought to return FALSE or NULL in cases * involving NULL inputs. Also, in an outer join's ON clause we can only * do this if the sublink is degenerate (ie, references only the nullable - * side of the join). In that case we can effectively push the semijoin + * side of the join). In that case it is legal to push the semijoin * down into the nullable side of the join. If the sublink references any * nonnullable-side variables then it would have to be evaluated as part * of the outer join, which makes things way too complicated. @@ -110,13 +110,22 @@ static Node *find_jointree_node_for_rel(Node *jtnode, int relid); void pull_up_sublinks(PlannerInfo *root) { + Node *jtnode; Relids relids; /* Begin recursion through the jointree */ - root->parse->jointree = (FromExpr *) - pull_up_sublinks_jointree_recurse(root, - (Node *) root->parse->jointree, - &relids); + jtnode = pull_up_sublinks_jointree_recurse(root, + (Node *) root->parse->jointree, + &relids); + + /* + * root->parse->jointree must always be a FromExpr, so insert a dummy one + * if we got a bare RangeTblRef or JoinExpr out of the recursion. + */ + if (IsA(jtnode, FromExpr)) + root->parse->jointree = (FromExpr *) jtnode; + else + root->parse->jointree = makeFromExpr(list_make1(jtnode), NULL); } /* @@ -144,9 +153,9 @@ pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode, { FromExpr *f = (FromExpr *) jtnode; List *newfromlist = NIL; - Node *newquals; - List *subfromlist = NIL; Relids frelids = NULL; + FromExpr *newf; + Node *jtlink; ListCell *l; /* First, recurse to process children and collect their relids */ @@ -161,26 +170,32 @@ pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode, newfromlist = lappend(newfromlist, newchild); frelids = bms_join(frelids, childrelids); } + /* Build the replacement FromExpr; no quals yet */ + newf = makeFromExpr(newfromlist, NULL); + /* Set up a link representing the rebuilt jointree */ + jtlink = (Node *) newf; /* Now process qual --- all children are available for use */ - newquals = pull_up_sublinks_qual_recurse(root, f->quals, frelids, - &subfromlist); - /* Any pulled-up subqueries can just be attached to the fromlist */ - newfromlist = list_concat(newfromlist, subfromlist); + newf->quals = pull_up_sublinks_qual_recurse(root, f->quals, frelids, + &jtlink); /* + * Note that the result will be either newf, or a stack of JoinExprs + * with newf at the base. We rely on subsequent optimization steps + * to flatten this and rearrange the joins as needed. + * * Although we could include the pulled-up subqueries in the returned * relids, there's no need since upper quals couldn't refer to their * outputs anyway. */ *relids = frelids; - jtnode = (Node *) makeFromExpr(newfromlist, newquals); + jtnode = jtlink; } else if (IsA(jtnode, JoinExpr)) { JoinExpr *j; Relids leftrelids; Relids rightrelids; - List *subfromlist = NIL; + Node *jtlink; /* * Make a modifiable copy of join node, but don't bother copying @@ -188,6 +203,7 @@ pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode, */ j = (JoinExpr *) palloc(sizeof(JoinExpr)); memcpy(j, jtnode, sizeof(JoinExpr)); + jtlink = (Node *) j; /* Recurse to process children and collect their relids */ j->larg = pull_up_sublinks_jointree_recurse(root, j->larg, @@ -197,13 +213,15 @@ pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode, /* * Now process qual, showing appropriate child relids as available, - * and then attach any pulled-up jointree items at the right place. - * The pulled-up items must go below where the quals that refer to - * them will be placed. Since the JoinExpr itself can only handle - * two child nodes, we hack up a valid jointree by inserting dummy - * FromExprs that have no quals. These should get flattened out - * during deconstruct_recurse(), so they won't impose any extra - * overhead. + * and attach any pulled-up jointree items at the right place. + * In the inner-join case we put new JoinExprs above the existing one + * (much as for a FromExpr-style join). In outer-join cases the + * new JoinExprs must go into the nullable side of the outer join. + * The point of the available_rels machinations is to ensure that we + * only pull up quals for which that's okay. + * + * We don't expect to see any pre-existing JOIN_SEMI or JOIN_ANTI + * nodes here. */ switch (j->jointype) { @@ -211,22 +229,12 @@ pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode, j->quals = pull_up_sublinks_qual_recurse(root, j->quals, bms_union(leftrelids, rightrelids), - &subfromlist); - /* We arbitrarily put pulled-up subqueries into right child */ - if (subfromlist) - j->rarg = (Node *) makeFromExpr(lcons(j->rarg, - subfromlist), - NULL); + &jtlink); break; case JOIN_LEFT: j->quals = pull_up_sublinks_qual_recurse(root, j->quals, rightrelids, - &subfromlist); - /* Any pulled-up subqueries must go into right child */ - if (subfromlist) - j->rarg = (Node *) makeFromExpr(lcons(j->rarg, - subfromlist), - NULL); + &j->rarg); break; case JOIN_FULL: /* can't do anything with full-join quals */ @@ -234,12 +242,7 @@ pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode, case JOIN_RIGHT: j->quals = pull_up_sublinks_qual_recurse(root, j->quals, leftrelids, - &subfromlist); - /* Any pulled-up subqueries must go into left child */ - if (subfromlist) - j->larg = (Node *) makeFromExpr(lcons(j->larg, - subfromlist), - NULL); + &j->larg); break; default: elog(ERROR, "unrecognized join type: %d", @@ -255,9 +258,10 @@ pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode, * levels would mistakenly think they couldn't use references to this * join. */ - *relids = bms_add_member(bms_join(leftrelids, rightrelids), - j->rtindex); - jtnode = (Node *) j; + *relids = bms_join(leftrelids, rightrelids); + if (j->rtindex) + *relids = bms_add_member(*relids, j->rtindex); + jtnode = jtlink; } else elog(ERROR, "unrecognized node type: %d", @@ -268,40 +272,47 @@ pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode, /* * Recurse through top-level qual nodes for pull_up_sublinks() * - * Caller must have initialized *fromlist to NIL. We append any new - * jointree items to that list. + * jtlink points to the link in the jointree where any new JoinExprs should be + * inserted. If we find multiple pull-up-able SubLinks, they'll get stacked + * there in the order we encounter them. We rely on subsequent optimization + * to rearrange the stack if appropriate. */ static Node * pull_up_sublinks_qual_recurse(PlannerInfo *root, Node *node, - Relids available_rels, List **fromlist) + Relids available_rels, Node **jtlink) { if (node == NULL) return NULL; if (IsA(node, SubLink)) { SubLink *sublink = (SubLink *) node; - Node *new_qual; - List *new_fromlist; + JoinExpr *j; /* Is it a convertible ANY or EXISTS clause? */ if (sublink->subLinkType == ANY_SUBLINK) { - if (convert_ANY_sublink_to_join(root, sublink, - available_rels, - &new_qual, &new_fromlist)) + j = convert_ANY_sublink_to_join(root, sublink, + available_rels); + if (j) { - *fromlist = list_concat(*fromlist, new_fromlist); - return new_qual; + /* Yes, insert the new join node into the join tree */ + j->larg = *jtlink; + *jtlink = (Node *) j; + /* and return NULL representing constant TRUE */ + return NULL; } } else if (sublink->subLinkType == EXISTS_SUBLINK) { - if (convert_EXISTS_sublink_to_join(root, sublink, false, - available_rels, - &new_qual, &new_fromlist)) + j = convert_EXISTS_sublink_to_join(root, sublink, false, + available_rels); + if (j) { - *fromlist = list_concat(*fromlist, new_fromlist); - return new_qual; + /* Yes, insert the new join node into the join tree */ + j->larg = *jtlink; + *jtlink = (Node *) j; + /* and return NULL representing constant TRUE */ + return NULL; } } /* Else return it unmodified */ @@ -311,19 +322,21 @@ pull_up_sublinks_qual_recurse(PlannerInfo *root, Node *node, { /* If the immediate argument of NOT is EXISTS, try to convert */ SubLink *sublink = (SubLink *) get_notclausearg((Expr *) node); - Node *new_qual; - List *new_fromlist; + JoinExpr *j; if (sublink && IsA(sublink, SubLink)) { if (sublink->subLinkType == EXISTS_SUBLINK) { - if (convert_EXISTS_sublink_to_join(root, sublink, true, - available_rels, - &new_qual, &new_fromlist)) + j = convert_EXISTS_sublink_to_join(root, sublink, true, + available_rels); + if (j) { - *fromlist = list_concat(*fromlist, new_fromlist); - return new_qual; + /* Yes, insert the new join node into the join tree */ + j->larg = *jtlink; + *jtlink = (Node *) j; + /* and return NULL representing constant TRUE */ + return NULL; } } } @@ -339,14 +352,22 @@ pull_up_sublinks_qual_recurse(PlannerInfo *root, Node *node, foreach(l, ((BoolExpr *) node)->args) { Node *oldclause = (Node *) lfirst(l); + Node *newclause; - newclauses = lappend(newclauses, - pull_up_sublinks_qual_recurse(root, - oldclause, - available_rels, - fromlist)); + newclause = pull_up_sublinks_qual_recurse(root, + oldclause, + available_rels, + jtlink); + if (newclause) + newclauses = lappend(newclauses, newclause); } - return (Node *) make_andclause(newclauses); + /* We might have got back fewer clauses than we started with */ + if (newclauses == NIL) + return NULL; + else if (list_length(newclauses) == 1) + return (Node *) linitial(newclauses); + else + return (Node *) make_andclause(newclauses); } /* Stop if not an AND */ return node; @@ -489,6 +510,8 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode, below_outer_join, false); break; case JOIN_LEFT: + case JOIN_SEMI: + case JOIN_ANTI: j->larg = pull_up_subqueries(root, j->larg, below_outer_join, false); j->rarg = pull_up_subqueries(root, j->rarg, @@ -702,12 +725,12 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, parse->rowMarks = list_concat(parse->rowMarks, subquery->rowMarks); /* - * We also have to fix the relid sets of any FlattenedSubLink and - * PlaceHolderVar nodes in the parent query. (This could perhaps be done - * by ResolveNew, but it would clutter that routine's API unreasonably.) - * Note in particular that any PlaceHolderVar nodes just created by - * insert_targetlist_placeholders() will be adjusted, so having created - * them with the subquery's varno is correct. + * We also have to fix the relid sets of any PlaceHolderVar nodes in the + * parent query. (This could perhaps be done by ResolveNew, but it would + * clutter that routine's API unreasonably.) Note in particular that any + * PlaceHolderVar nodes just created by insert_targetlist_placeholders() + * will be adjusted, so having created them with the subquery's varno is + * correct. * * Likewise, relids appearing in AppendRelInfo nodes have to be fixed. * We already checked that this won't require introducing multiple @@ -1419,6 +1442,14 @@ reduce_outer_joins_pass2(Node *jtnode, jointype = JOIN_RIGHT; } break; + case JOIN_SEMI: + case JOIN_ANTI: + /* + * These could only have been introduced by pull_up_sublinks, + * so there's no way that upper quals could refer to their + * righthand sides, and no point in checking. + */ + break; default: elog(ERROR, "unrecognized join type: %d", (int) jointype); @@ -1475,14 +1506,15 @@ reduce_outer_joins_pass2(Node *jtnode, } /* Apply the jointype change, if any, to both jointree node and RTE */ - if (jointype != j->jointype) + if (rtindex && jointype != j->jointype) { RangeTblEntry *rte = rt_fetch(rtindex, root->parse->rtable); Assert(rte->rtekind == RTE_JOIN); Assert(rte->jointype == j->jointype); - rte->jointype = j->jointype = jointype; + rte->jointype = jointype; } + j->jointype = jointype; /* Only recurse if there's more to do below here */ if (left_state->contains_outer || right_state->contains_outer) @@ -1542,7 +1574,7 @@ reduce_outer_joins_pass2(Node *jtnode, pass_nonnullable_vars = local_nonnullable_vars; pass_forced_null_vars = local_forced_null_vars; } - else if (jointype != JOIN_FULL) /* ie, LEFT or ANTI */ + else if (jointype != JOIN_FULL) /* ie, LEFT/SEMI/ANTI */ { /* can't pass local constraints to non-nullable side */ pass_nonnullable_rels = nonnullable_rels; @@ -1564,7 +1596,7 @@ reduce_outer_joins_pass2(Node *jtnode, if (right_state->contains_outer) { - if (jointype != JOIN_FULL) /* ie, INNER, LEFT or ANTI */ + if (jointype != JOIN_FULL) /* ie, INNER/LEFT/SEMI/ANTI */ { /* pass appropriate constraints, per comment above */ pass_nonnullable_rels = local_nonnullable_rels; @@ -1595,10 +1627,10 @@ reduce_outer_joins_pass2(Node *jtnode, * substitute_multiple_relids - adjust node relid sets after pulling up * a subquery * - * Find any FlattenedSubLink or PlaceHolderVar nodes in the given tree that - * reference the pulled-up relid, and change them to reference the replacement - * relid(s). We do not need to recurse into subqueries, since no subquery of - * the current top query could (yet) contain such a reference. + * Find any PlaceHolderVar nodes in the given tree that reference the + * pulled-up relid, and change them to reference the replacement relid(s). + * We do not need to recurse into subqueries, since no subquery of the current + * top query could (yet) contain such a reference. * * NOTE: although this has the form of a walker, we cheat and modify the * nodes in-place. This should be OK since the tree was copied by ResolveNew @@ -1618,26 +1650,6 @@ substitute_multiple_relids_walker(Node *node, { if (node == NULL) return false; - if (IsA(node, FlattenedSubLink)) - { - FlattenedSubLink *fslink = (FlattenedSubLink *) node; - - if (bms_is_member(context->varno, fslink->lefthand)) - { - fslink->lefthand = bms_union(fslink->lefthand, - context->subrelids); - fslink->lefthand = bms_del_member(fslink->lefthand, - context->varno); - } - if (bms_is_member(context->varno, fslink->righthand)) - { - fslink->righthand = bms_union(fslink->righthand, - context->subrelids); - fslink->righthand = bms_del_member(fslink->righthand, - context->varno); - } - /* fall through to examine children */ - } if (IsA(node, PlaceHolderVar)) { PlaceHolderVar *phv = (PlaceHolderVar *) node; @@ -1757,7 +1769,7 @@ get_relids_in_jointree(Node *jtnode, bool include_joins) result = get_relids_in_jointree(j->larg, include_joins); result = bms_join(result, get_relids_in_jointree(j->rarg, include_joins)); - if (include_joins) + if (include_joins && j->rtindex) result = bms_add_member(result, j->rtindex); } else diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index b9ce6d2ed0c..13d08eef3ee 100644 --- a/src/backend/optimizer/prep/prepunion.c +++ b/src/backend/optimizer/prep/prepunion.c @@ -22,7 +22,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.165 2009/02/06 23:43:23 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.166 2009/02/25 03:30:37 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1587,23 +1587,6 @@ adjust_appendrel_attrs_mutator(Node *node, AppendRelInfo *context) j->rtindex = context->child_relid; return (Node *) j; } - if (IsA(node, FlattenedSubLink)) - { - /* Copy the FlattenedSubLink node with correct mutation of subnodes */ - FlattenedSubLink *fslink; - - fslink = (FlattenedSubLink *) expression_tree_mutator(node, - adjust_appendrel_attrs_mutator, - (void *) context); - /* now fix FlattenedSubLink's relid sets */ - fslink->lefthand = adjust_relid_set(fslink->lefthand, - context->parent_relid, - context->child_relid); - fslink->righthand = adjust_relid_set(fslink->righthand, - context->parent_relid, - context->child_relid); - return (Node *) fslink; - } if (IsA(node, PlaceHolderVar)) { /* Copy the PlaceHolderVar node with correct mutation of subnodes */ diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 65c9b614584..c9c7270d2b8 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.275 2009/01/09 15:46:10 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.276 2009/02/25 03:30:37 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -1300,15 +1300,6 @@ find_nonnullable_rels_walker(Node *node, bool top_level) expr->booltesttype == IS_NOT_UNKNOWN)) result = find_nonnullable_rels_walker((Node *) expr->arg, false); } - else if (IsA(node, FlattenedSubLink)) - { - /* JOIN_SEMI sublinks preserve strictness, but JOIN_ANTI ones don't */ - FlattenedSubLink *expr = (FlattenedSubLink *) node; - - if (expr->jointype == JOIN_SEMI) - result = find_nonnullable_rels_walker((Node *) expr->quals, - top_level); - } else if (IsA(node, PlaceHolderVar)) { PlaceHolderVar *phv = (PlaceHolderVar *) node; @@ -1511,15 +1502,6 @@ find_nonnullable_vars_walker(Node *node, bool top_level) expr->booltesttype == IS_NOT_UNKNOWN)) result = find_nonnullable_vars_walker((Node *) expr->arg, false); } - else if (IsA(node, FlattenedSubLink)) - { - /* JOIN_SEMI sublinks preserve strictness, but JOIN_ANTI ones don't */ - FlattenedSubLink *expr = (FlattenedSubLink *) node; - - if (expr->jointype == JOIN_SEMI) - result = find_nonnullable_vars_walker((Node *) expr->quals, - top_level); - } else if (IsA(node, PlaceHolderVar)) { PlaceHolderVar *phv = (PlaceHolderVar *) node; @@ -2943,24 +2925,6 @@ eval_const_expressions_mutator(Node *node, newbtest->booltesttype = btest->booltesttype; return (Node *) newbtest; } - if (IsA(node, FlattenedSubLink)) - { - FlattenedSubLink *fslink = (FlattenedSubLink *) node; - FlattenedSubLink *newfslink; - Expr *quals; - - /* Simplify and also canonicalize the arguments */ - quals = (Expr *) eval_const_expressions_mutator((Node *) fslink->quals, - context); - quals = canonicalize_qual(quals); - - newfslink = makeNode(FlattenedSubLink); - newfslink->jointype = fslink->jointype; - newfslink->lefthand = fslink->lefthand; - newfslink->righthand = fslink->righthand; - newfslink->quals = quals; - return (Node *) newfslink; - } if (IsA(node, PlaceHolderVar) && context->estimate) { /* diff --git a/src/backend/optimizer/util/var.c b/src/backend/optimizer/util/var.c index 64d6bba1480..7768712b569 100644 --- a/src/backend/optimizer/util/var.c +++ b/src/backend/optimizer/util/var.c @@ -14,7 +14,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/util/var.c,v 1.83 2009/01/01 17:23:45 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/util/var.c,v 1.84 2009/02/25 03:30:37 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -766,24 +766,6 @@ flatten_join_alias_vars_mutator(Node *node, /* Recurse in case join input is itself a join */ return flatten_join_alias_vars_mutator(newvar, context); } - if (IsA(node, FlattenedSubLink)) - { - /* Copy the FlattenedSubLink node with correct mutation of subnodes */ - FlattenedSubLink *fslink; - - fslink = (FlattenedSubLink *) expression_tree_mutator(node, - flatten_join_alias_vars_mutator, - (void *) context); - /* now fix FlattenedSubLink's relid sets */ - if (context->sublevels_up == 0) - { - fslink->lefthand = alias_relid_set(context->root, - fslink->lefthand); - fslink->righthand = alias_relid_set(context->root, - fslink->righthand); - } - return (Node *) fslink; - } if (IsA(node, PlaceHolderVar)) { /* Copy the PlaceHolderVar node with correct mutation of subnodes */ diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c index e0403927f32..72cddaaa34c 100644 --- a/src/backend/rewrite/rewriteManip.c +++ b/src/backend/rewrite/rewriteManip.c @@ -7,7 +7,7 @@ * * * 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) { 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 31881976bd8..11e31e0a65a 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * 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 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 0d8efbe9880..3315329923e 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -10,7 +10,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * 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 * * 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 f00d1becc70..e8e20d202b1 100644 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * 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 Path *cheapest_total_innerpath; /* cheapest total cost */ } 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 @@ -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 54965a5fe71..a3188535965 100644 --- a/src/include/optimizer/subselect.h +++ b/src/include/optimizer/subselect.h @@ -5,7 +5,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * 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 @@ #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 12b54aad172..8e6fc13bb38 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 2721c3f1cf0..3032beacd42 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 245fde58ae4..149530e14db 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);