diff options
Diffstat (limited to 'src/backend/optimizer/plan/initsplan.c')
-rw-r--r-- | src/backend/optimizer/plan/initsplan.c | 149 |
1 files changed, 102 insertions, 47 deletions
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c index 0b315f2cd7d..f991f30e130 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.123.2.5 2007/05/22 23:24:08 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/plan/initsplan.c,v 1.123.2.6 2007/08/31 01:44:14 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -40,9 +40,11 @@ int join_collapse_limit; static void add_vars_to_targetlist(PlannerInfo *root, List *vars, Relids where_needed); 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, @@ -206,13 +208,14 @@ List * 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); } /* @@ -226,20 +229,24 @@ deconstruct_jointree(PlannerInfo *root) * 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)) @@ -248,6 +255,8 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join, /* 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)) @@ -263,6 +272,7 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join, * 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) @@ -273,7 +283,8 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join, 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--; @@ -285,6 +296,16 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join, } /* + * 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. */ foreach(l, (List *) f->quals) @@ -297,6 +318,8 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join, JoinExpr *j = (JoinExpr *) jtnode; Relids leftids, rightids, + left_inners, + right_inners, nonnullable_rels, ojscope; List *leftjoinlist, @@ -321,32 +344,35 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join, 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; @@ -354,11 +380,12 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join, /* 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: @@ -377,8 +404,11 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join, */ 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 @@ -447,6 +477,7 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join, * 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 * @@ -459,17 +490,19 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join, * * 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; /* @@ -499,6 +532,8 @@ make_outerjoininfo(PlannerInfo *root, 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) { @@ -523,21 +558,17 @@ make_outerjoininfo(PlannerInfo *root, 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) { @@ -550,23 +581,29 @@ make_outerjoininfo(PlannerInfo *root, /* * 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 @@ -579,20 +616,38 @@ make_outerjoininfo(PlannerInfo *root, * effect is therefore sufficiently represented by the * delay_upper_joins flag saved for us by distribute_qual_to_rels. */ - 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; } |