Adjust join_search_one_level's handling of clauseless joins.
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 21 Apr 2012 00:10:46 +0000 (20:10 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 21 Apr 2012 00:10:46 +0000 (20:10 -0400)
For an initial relation that lacks any join clauses (that is, it has to be
cartesian-product-joined to the rest of the query), we considered only
cartesian joins with initial rels appearing later in the initial-relations
list.  This creates an undesirable dependency on FROM-list order.  We would
never fail to find a plan, but perhaps we might not find the best available
plan.  Noted while discussing the logic with Amit Kapila.

Improve the comments a bit in this area, too.

Arguably this is a bug fix, but given the lack of complaints from the
field I'll refrain from back-patching.

src/backend/optimizer/path/joinrels.c

index 82252753fb0d331875c1607a7f7169601b6183d6..24d46515070e8602d31db2fc9dc264c9ec6543b5 100644 (file)
@@ -65,33 +65,34 @@ join_search_one_level(PlannerInfo *root, int level)
     * We prefer to join using join clauses, but if we find a rel of level-1
     * members that has no join clauses, we will generate Cartesian-product
     * joins against all initial rels not already contained in it.
-    *
-    * In the first pass (level == 2), we try to join each initial rel to each
-    * initial rel that appears later in joinrels[1].  (The mirror-image joins
-    * are handled automatically by make_join_rel.)  In later passes, we try
-    * to join rels of size level-1 from joinrels[level-1] to each initial rel
-    * in joinrels[1].
     */
    foreach(r, joinrels[level - 1])
    {
        RelOptInfo *old_rel = (RelOptInfo *) lfirst(r);
-       ListCell   *other_rels;
-
-       if (level == 2)
-           other_rels = lnext(r);      /* only consider remaining initial
-                                        * rels */
-       else
-           other_rels = list_head(joinrels[1]);        /* consider all initial
-                                                        * rels */
 
        if (old_rel->joininfo != NIL || old_rel->has_eclass_joins ||
            has_join_restriction(root, old_rel))
        {
            /*
-            * There are relevant join clauses or join order restrictions,
-            * so consider joins between this rel and (only) those rels it is
-            * linked to by a clause or restriction.
+            * There are join clauses or join order restrictions relevant to
+            * this rel, so consider joins between this rel and (only) those
+            * initial rels it is linked to by a clause or restriction.
+            *
+            * At level 2 this condition is symmetric, so there is no need to
+            * look at initial rels before this one in the list; we already
+            * considered such joins when we were at the earlier rel.  (The
+            * mirror-image joins are handled automatically by make_join_rel.)
+            * In later passes (level > 2), we join rels of the previous level
+            * to each initial rel they don't already include but have a join
+            * clause or restriction with.
             */
+           ListCell   *other_rels;
+
+           if (level == 2)     /* consider remaining initial rels */
+               other_rels = lnext(r);
+           else                /* consider all initial rels */
+               other_rels = list_head(joinrels[1]);
+
            make_rels_by_clause_joins(root,
                                      old_rel,
                                      other_rels);
@@ -102,10 +103,17 @@ join_search_one_level(PlannerInfo *root, int level)
             * Oops, we have a relation that is not joined to any other
             * relation, either directly or by join-order restrictions.
             * Cartesian product time.
+            *
+            * We consider a cartesian product with each not-already-included
+            * initial rel, whether it has other join clauses or not.  At
+            * level 2, if there are two or more clauseless initial rels, we
+            * will redundantly consider joining them in both directions; but
+            * such cases aren't common enough to justify adding complexity to
+            * avoid the duplicated effort.
             */
            make_rels_by_clauseless_joins(root,
                                          old_rel,
-                                         other_rels);
+                                         list_head(joinrels[1]));
        }
    }
 
@@ -135,7 +143,7 @@ join_search_one_level(PlannerInfo *root, int level)
            ListCell   *r2;
 
            /*
-            * We can ignore clauseless joins here, *except* when they
+            * We can ignore relations without join clauses here, unless they
             * participate in join-order restrictions --- then we might have
             * to force a bushy join plan.
             */