*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/plan/initsplan.c,v 1.132 2007/05/22 23:23:56 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/plan/initsplan.c,v 1.133 2007/08/31 01:44:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
static List *deconstruct_recurse(PlannerInfo *root, Node *jtnode,
- bool below_outer_join, Relids *qualscope);
+ bool below_outer_join,
+ Relids *qualscope, Relids *inner_join_rels);
static OuterJoinInfo *make_outerjoininfo(PlannerInfo *root,
Relids left_rels, Relids right_rels,
+ Relids inner_join_rels,
bool is_full_join, Node *clause);
static void distribute_qual_to_rels(PlannerInfo *root, Node *clause,
bool is_deduced,
deconstruct_jointree(PlannerInfo *root)
{
Relids qualscope;
+ Relids inner_join_rels;
/* Start recursion at top of jointree */
Assert(root->parse->jointree != NULL &&
IsA(root->parse->jointree, FromExpr));
return deconstruct_recurse(root, (Node *) root->parse->jointree, false,
- &qualscope);
+ &qualscope, &inner_join_rels);
}
/*
* Outputs:
* *qualscope gets the set of base Relids syntactically included in this
* jointree node (do not modify or free this, as it may also be pointed
- * to by RestrictInfo nodes)
+ * to by RestrictInfo and OuterJoinInfo nodes)
+ * *inner_join_rels gets the set of base Relids syntactically included in
+ * inner joins appearing at or below this jointree node (do not modify
+ * or free this, either)
* Return value is the appropriate joinlist for this jointree node
*
* In addition, entries will be added to root->oj_info_list for outer joins.
*/
static List *
deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
- Relids *qualscope)
+ Relids *qualscope, Relids *inner_join_rels)
{
List *joinlist;
if (jtnode == NULL)
{
*qualscope = NULL;
+ *inner_join_rels = NULL;
return NIL;
}
if (IsA(jtnode, RangeTblRef))
/* No quals to deal with, just return correct result */
*qualscope = bms_make_singleton(varno);
+ /* A single baserel does not create an inner join */
+ *inner_join_rels = NULL;
joinlist = list_make1(jtnode);
}
else if (IsA(jtnode, FromExpr))
* subproblems, since that won't lengthen the joinlist anyway.
*/
*qualscope = NULL;
+ *inner_join_rels = NULL;
joinlist = NIL;
remaining = list_length(f->fromlist);
foreach(l, f->fromlist)
sub_joinlist = deconstruct_recurse(root, lfirst(l),
below_outer_join,
- &sub_qualscope);
+ &sub_qualscope,
+ inner_join_rels);
*qualscope = bms_add_members(*qualscope, sub_qualscope);
sub_members = list_length(sub_joinlist);
remaining--;
joinlist = lappend(joinlist, sub_joinlist);
}
+ /*
+ * A FROM with more than one list element is an inner join subsuming
+ * all below it, so we should report inner_join_rels = qualscope.
+ * If there was exactly one element, we should (and already did) report
+ * whatever its inner_join_rels were. If there were no elements
+ * (is that possible?) the initialization before the loop fixed it.
+ */
+ if (list_length(f->fromlist) > 1)
+ *inner_join_rels = *qualscope;
+
/*
* Now process the top-level quals.
*/
JoinExpr *j = (JoinExpr *) jtnode;
Relids leftids,
rightids,
+ left_inners,
+ right_inners,
nonnullable_rels,
ojscope;
List *leftjoinlist,
case JOIN_INNER:
leftjoinlist = deconstruct_recurse(root, j->larg,
below_outer_join,
- &leftids);
+ &leftids, &left_inners);
rightjoinlist = deconstruct_recurse(root, j->rarg,
below_outer_join,
- &rightids);
+ &rightids, &right_inners);
*qualscope = bms_union(leftids, rightids);
+ *inner_join_rels = *qualscope;
/* Inner join adds no restrictions for quals */
nonnullable_rels = NULL;
break;
case JOIN_LEFT:
leftjoinlist = deconstruct_recurse(root, j->larg,
below_outer_join,
- &leftids);
+ &leftids, &left_inners);
rightjoinlist = deconstruct_recurse(root, j->rarg,
true,
- &rightids);
+ &rightids, &right_inners);
*qualscope = bms_union(leftids, rightids);
+ *inner_join_rels = bms_union(left_inners, right_inners);
nonnullable_rels = leftids;
break;
case JOIN_FULL:
leftjoinlist = deconstruct_recurse(root, j->larg,
true,
- &leftids);
+ &leftids, &left_inners);
rightjoinlist = deconstruct_recurse(root, j->rarg,
true,
- &rightids);
+ &rightids, &right_inners);
*qualscope = bms_union(leftids, rightids);
+ *inner_join_rels = bms_union(left_inners, right_inners);
/* each side is both outer and inner */
nonnullable_rels = *qualscope;
break;
/* notice we switch leftids and rightids */
leftjoinlist = deconstruct_recurse(root, j->larg,
true,
- &rightids);
+ &rightids, &right_inners);
rightjoinlist = deconstruct_recurse(root, j->rarg,
below_outer_join,
- &leftids);
+ &leftids, &left_inners);
*qualscope = bms_union(leftids, rightids);
+ *inner_join_rels = bms_union(left_inners, right_inners);
nonnullable_rels = leftids;
break;
default:
*/
if (j->jointype != JOIN_INNER)
{
- ojinfo = make_outerjoininfo(root, leftids, rightids,
- (j->jointype == JOIN_FULL), j->quals);
+ ojinfo = make_outerjoininfo(root,
+ leftids, rightids,
+ *inner_join_rels,
+ (j->jointype == JOIN_FULL),
+ j->quals);
ojscope = bms_union(ojinfo->min_lefthand, ojinfo->min_righthand);
}
else
* Inputs:
* left_rels: the base Relids syntactically on outer side of join
* right_rels: the base Relids syntactically on inner side of join
+ * inner_join_rels: base Relids participating in inner joins below this one
* is_full_join: what it says
* clause: the outer join's join condition
*
*
* Note: we assume that this function is invoked bottom-up, so that
* root->oj_info_list already contains entries for all outer joins that are
- * syntactically below this one; and indeed that oj_info_list is ordered
- * with syntactically lower joins listed first.
+ * syntactically below this one.
*/
static OuterJoinInfo *
make_outerjoininfo(PlannerInfo *root,
Relids left_rels, Relids right_rels,
+ Relids inner_join_rels,
bool is_full_join, Node *clause)
{
OuterJoinInfo *ojinfo = makeNode(OuterJoinInfo);
Relids clause_relids;
Relids strict_relids;
+ Relids min_lefthand;
+ Relids min_righthand;
ListCell *l;
/*
ojinfo->delay_upper_joins = false;
/* If it's a full join, no need to be very smart */
+ ojinfo->syn_lefthand = left_rels;
+ ojinfo->syn_righthand = right_rels;
ojinfo->is_full_join = is_full_join;
if (is_full_join)
{
ojinfo->lhs_strict = bms_overlap(strict_relids, left_rels);
/*
- * Required LHS is basically the LHS rels mentioned in the clause... but
- * if there aren't any, punt and make it the full LHS, to avoid having an
- * empty min_lefthand which will confuse later processing. (We don't try
- * to be smart about such cases, just correct.) We may have to add more
- * rels based on lower outer joins; see below.
+ * Required LHS always includes the LHS rels mentioned in the clause.
+ * We may have to add more rels based on lower outer joins; see below.
*/
- ojinfo->min_lefthand = bms_intersect(clause_relids, left_rels);
- if (bms_is_empty(ojinfo->min_lefthand))
- ojinfo->min_lefthand = bms_copy(left_rels);
+ min_lefthand = bms_intersect(clause_relids, left_rels);
/*
- * Required RHS is normally the full set of RHS rels. Sometimes we can
- * exclude some, see below.
+ * Similarly for required RHS. But here, we must also include any lower
+ * inner joins, to ensure we don't try to commute with any of them.
*/
- ojinfo->min_righthand = bms_copy(right_rels);
+ min_righthand = bms_int_members(bms_union(clause_relids, inner_join_rels),
+ right_rels);
foreach(l, root->oj_info_list)
{
/*
* For a lower OJ in our LHS, if our join condition uses the lower
* join's RHS and is not strict for that rel, we must preserve the
- * ordering of the two OJs, so add lower OJ's full required relset to
- * min_lefthand.
+ * ordering of the two OJs, so add lower OJ's full syntactic relset to
+ * min_lefthand. (We must use its full syntactic relset, not just
+ * its min_lefthand + min_righthand. This is because there might
+ * be other OJs below this one that this one can commute with,
+ * but we cannot commute with them if we don't with this one.)
+ *
+ * Note: I believe we have to insist on being strict for at least one
+ * rel in the lower OJ's min_righthand, not its whole syn_righthand.
*/
- if (bms_overlap(ojinfo->min_lefthand, otherinfo->min_righthand) &&
+ if (bms_overlap(left_rels, otherinfo->syn_righthand) &&
!bms_overlap(strict_relids, otherinfo->min_righthand))
{
- ojinfo->min_lefthand = bms_add_members(ojinfo->min_lefthand,
- otherinfo->min_lefthand);
- ojinfo->min_lefthand = bms_add_members(ojinfo->min_lefthand,
- otherinfo->min_righthand);
+ min_lefthand = bms_add_members(min_lefthand,
+ otherinfo->syn_lefthand);
+ min_lefthand = bms_add_members(min_lefthand,
+ otherinfo->syn_righthand);
}
/*
* For a lower OJ in our RHS, if our join condition does not use the
* lower join's RHS and the lower OJ's join condition is strict, we
- * can interchange the ordering of the two OJs, so exclude the lower
- * RHS from our min_righthand.
+ * can interchange the ordering of the two OJs; otherwise we must
+ * add lower OJ's full syntactic relset to min_righthand.
*
* Here, we have to consider that "our join condition" includes
* any clauses that syntactically appeared above the lower OJ and
* effect is therefore sufficiently represented by the
* delay_upper_joins flag saved for us by check_outerjoin_delay.
*/
- if (bms_overlap(ojinfo->min_righthand, otherinfo->min_righthand) &&
- !bms_overlap(clause_relids, otherinfo->min_righthand) &&
- otherinfo->lhs_strict && !otherinfo->delay_upper_joins)
+ if (bms_overlap(right_rels, otherinfo->syn_righthand))
{
- ojinfo->min_righthand = bms_del_members(ojinfo->min_righthand,
- otherinfo->min_righthand);
+ if (bms_overlap(clause_relids, otherinfo->syn_righthand) ||
+ !otherinfo->lhs_strict || otherinfo->delay_upper_joins)
+ {
+ min_righthand = bms_add_members(min_righthand,
+ otherinfo->syn_lefthand);
+ min_righthand = bms_add_members(min_righthand,
+ otherinfo->syn_righthand);
+ }
}
}
- /* Neither set should be empty, else we might get confused later */
- Assert(!bms_is_empty(ojinfo->min_lefthand));
- Assert(!bms_is_empty(ojinfo->min_righthand));
+ /*
+ * If we found nothing to put in min_lefthand, punt and make it the full
+ * LHS, to avoid having an empty min_lefthand which will confuse later
+ * processing. (We don't try to be smart about such cases, just correct.)
+ * Likewise for min_righthand.
+ */
+ if (bms_is_empty(min_lefthand))
+ min_lefthand = bms_copy(left_rels);
+ if (bms_is_empty(min_righthand))
+ min_righthand = bms_copy(right_rels);
+
+ /* Now they'd better be nonempty */
+ Assert(!bms_is_empty(min_lefthand));
+ Assert(!bms_is_empty(min_righthand));
/* Shouldn't overlap either */
- Assert(!bms_overlap(ojinfo->min_lefthand, ojinfo->min_righthand));
+ Assert(!bms_overlap(min_lefthand, min_righthand));
+
+ ojinfo->min_lefthand = min_lefthand;
+ ojinfo->min_righthand = min_righthand;
return ojinfo;
}