diff --git a/src/backend/optimizer/geqo/geqo_eval.c b/src/backend/optimizer/geqo/geqo_eval.c
index 240672edaf5248dfd131592c7ff3e2d78e677977..197efac43454fb85a1705af179f105baa06ea529 100644
--- a/src/backend/optimizer/geqo/geqo_eval.c
+++ b/src/backend/optimizer/geqo/geqo_eval.c
@@ -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;
 
 	/*
diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c
index 17b5f31915a23840919909c2062e667428d74cef..980179599436054a66da44a93d86eeaa6e9d630c 100644
--- a/src/backend/optimizer/path/joinrels.c
+++ b/src/backend/optimizer/path/joinrels.c
@@ -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;
 
diff --git a/src/backend/optimizer/util/joininfo.c b/src/backend/optimizer/util/joininfo.c
index 9aab37753de1a4c0041a242599b66124244b3c8e..bd54a1384b431a1fe567523ce263424dd62f6b1b 100644
--- a/src/backend/optimizer/util/joininfo.c
+++ b/src/backend/optimizer/util/joininfo.c
@@ -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;
diff --git a/src/include/optimizer/joininfo.h b/src/include/optimizer/joininfo.h
index 7c4909ec1c29a15cd53e62dc2035a812cabb691c..1480f77835a5818b0a677301ccc4c6c4ff6a7ed1 100644
--- a/src/include/optimizer/joininfo.h
+++ b/src/include/optimizer/joininfo.h
@@ -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,