Fix planner to do the right thing when a degenerate outer join (one whose
authorTom Lane
Tue, 12 Dec 2006 21:31:02 +0000 (21:31 +0000)
committerTom Lane
Tue, 12 Dec 2006 21:31:02 +0000 (21:31 +0000)
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.

src/backend/optimizer/geqo/geqo_eval.c
src/backend/optimizer/path/joinrels.c
src/backend/optimizer/util/joininfo.c
src/include/optimizer/joininfo.h

index 240672edaf5248dfd131592c7ff3e2d78e677977..197efac43454fb85a1705af179f105baa06ea529 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/backend/optimizer/geqo/geqo_eval.c,v 1.81 2006/10/24 17:50:22 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/geqo/geqo_eval.c,v 1.82 2006/12/12 21:31:02 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -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;
 
    /*
index 17b5f31915a23840919909c2062e667428d74cef..980179599436054a66da44a93d86eeaa6e9d630c 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/optimizer/path/joinrels.c,v 1.81 2006/10/24 17:50:22 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/optimizer/path/joinrels.c,v 1.82 2006/12/12 21:31:02 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -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;
 
index 9aab37753de1a4c0041a242599b66124244b3c8e..bd54a1384b431a1fe567523ce263424dd62f6b1b 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/optimizer/util/joininfo.c,v 1.44 2006/03/05 15:58:31 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/optimizer/util/joininfo.c,v 1.45 2006/12/12 21:31:02 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -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;
index 7c4909ec1c29a15cd53e62dc2035a812cabb691c..1480f77835a5818b0a677301ccc4c6c4ff6a7ed1 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/optimizer/joininfo.h,v 1.31 2006/03/05 15:58:57 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/joininfo.h,v 1.32 2006/12/12 21:31:02 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -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,