diff options
author | Tom Lane | 2006-12-12 21:31:09 +0000 |
---|---|---|
committer | Tom Lane | 2006-12-12 21:31:09 +0000 |
commit | 7c2abc5eeb25bb0e5aee109ab4825471d1363afa (patch) | |
tree | 77b38a3e4b6926c45abf68a2b1e5e738e38cd3f0 | |
parent | 2d57125e43d8bb1838add2696a64f31166c827a3 (diff) |
Fix planner to do the right thing when a degenerate outer join (one whose
joinclause doesn't use any outer-side vars) requires a "bushy" plan to be
created. The normal heuristic to avoid joins with no joinclause has to be
overridden in that case. Problem is new in 8.2; before that we forced the
outer join order anyway. Per example from Teodor.
-rw-r--r-- | src/backend/optimizer/geqo/geqo_eval.c | 2 | ||||
-rw-r--r-- | src/backend/optimizer/path/joinrels.c | 13 | ||||
-rw-r--r-- | src/backend/optimizer/util/joininfo.c | 37 | ||||
-rw-r--r-- | src/include/optimizer/joininfo.h | 3 |
4 files changed, 48 insertions, 7 deletions
diff --git a/src/backend/optimizer/geqo/geqo_eval.c b/src/backend/optimizer/geqo/geqo_eval.c index 3d7d710312..cf45538055 100644 --- a/src/backend/optimizer/geqo/geqo_eval.c +++ b/src/backend/optimizer/geqo/geqo_eval.c @@ -258,7 +258,7 @@ desirable_join(PlannerInfo *root, /* * Join if there is an applicable join clause. */ - if (have_relevant_joinclause(outer_rel, inner_rel)) + if (have_relevant_joinclause(root, outer_rel, inner_rel)) return true; /* diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c index f0c37b4fdc..7b02f9eddc 100644 --- a/src/backend/optimizer/path/joinrels.c +++ b/src/backend/optimizer/path/joinrels.c @@ -147,8 +147,13 @@ make_rels_by_joins(PlannerInfo *root, int level, List **joinrels) ListCell *other_rels; ListCell *r2; - if (old_rel->joininfo == NIL) - continue; /* we ignore clauseless joins here */ + /* + * We can ignore clauseless joins here, *except* when there are + * outer joins --- then we might have to force a bushy outer + * join. See have_relevant_joinclause(). + */ + if (old_rel->joininfo == NIL && root->oj_info_list == NIL) + continue; if (k == other_level) other_rels = lnext(r); /* only consider remaining rels */ @@ -166,7 +171,7 @@ make_rels_by_joins(PlannerInfo *root, int level, List **joinrels) * pair of rels. Do so if there is at least one usable * join clause. */ - if (have_relevant_joinclause(old_rel, new_rel)) + if (have_relevant_joinclause(root, old_rel, new_rel)) { RelOptInfo *jrel; @@ -270,7 +275,7 @@ make_rels_by_clause_joins(PlannerInfo *root, RelOptInfo *other_rel = (RelOptInfo *) lfirst(l); if (!bms_overlap(old_rel->relids, other_rel->relids) && - have_relevant_joinclause(old_rel, other_rel)) + have_relevant_joinclause(root, old_rel, other_rel)) { RelOptInfo *jrel; diff --git a/src/backend/optimizer/util/joininfo.c b/src/backend/optimizer/util/joininfo.c index b8e101b765..0dbbe5fcf6 100644 --- a/src/backend/optimizer/util/joininfo.c +++ b/src/backend/optimizer/util/joininfo.c @@ -24,7 +24,8 @@ * the two given relations. */ bool -have_relevant_joinclause(RelOptInfo *rel1, RelOptInfo *rel2) +have_relevant_joinclause(PlannerInfo *root, + RelOptInfo *rel1, RelOptInfo *rel2) { bool result = false; Relids join_relids; @@ -53,6 +54,40 @@ have_relevant_joinclause(RelOptInfo *rel1, RelOptInfo *rel2) } } + /* + * It's possible that the rels correspond to the left and right sides + * of a degenerate outer join, that is, one with no joinclause mentioning + * the non-nullable side. The above scan will then have failed to locate + * any joinclause indicating we should join, but nonetheless we must + * allow the join to occur. + * + * Note: we need no comparable check for IN-joins because we can handle + * sequential buildup of an IN-join to multiple outer-side rels; therefore + * the "last ditch" case in make_rels_by_joins() always succeeds. We + * could dispense with this hack if we were willing to try bushy plans + * in the "last ditch" case, but that seems too expensive. + */ + if (!result) + { + foreach(l, root->oj_info_list) + { + OuterJoinInfo *ojinfo = (OuterJoinInfo *) lfirst(l); + + /* ignore full joins --- other mechanisms handle them */ + if (ojinfo->is_full_join) + continue; + + if ((bms_is_subset(ojinfo->min_lefthand, rel1->relids) && + bms_is_subset(ojinfo->min_righthand, rel2->relids)) || + (bms_is_subset(ojinfo->min_lefthand, rel2->relids) && + bms_is_subset(ojinfo->min_righthand, rel1->relids))) + { + result = true; + break; + } + } + } + bms_free(join_relids); return result; diff --git a/src/include/optimizer/joininfo.h b/src/include/optimizer/joininfo.h index 3d765e458e..c6b417f7b9 100644 --- a/src/include/optimizer/joininfo.h +++ b/src/include/optimizer/joininfo.h @@ -17,7 +17,8 @@ #include "nodes/relation.h" -extern bool have_relevant_joinclause(RelOptInfo *rel1, RelOptInfo *rel2); +extern bool have_relevant_joinclause(PlannerInfo *root, + RelOptInfo *rel1, RelOptInfo *rel2); extern void add_join_clause_to_rels(PlannerInfo *root, RestrictInfo *restrictinfo, |