IndexClauseSet *jclauseset,
                            IndexClauseSet *eclauseset,
                            List **bitindexpaths);
-static void expand_eclass_clause_combinations(PlannerInfo *root,
-                                 RelOptInfo *rel,
-                                 IndexOptInfo *index,
-                                 int thiscol, int lastcol,
-                                 IndexClauseSet *clauseset,
-                                 IndexClauseSet *eclauseset,
-                                 List **bitindexpaths);
+static void consider_index_join_outer_rels(PlannerInfo *root, RelOptInfo *rel,
+                              IndexOptInfo *index,
+                              IndexClauseSet *rclauseset,
+                              IndexClauseSet *jclauseset,
+                              IndexClauseSet *eclauseset,
+                              List **bitindexpaths,
+                              List *indexjoinclauses,
+                              List **considered_relids);
+static void get_join_index_paths(PlannerInfo *root, RelOptInfo *rel,
+                    IndexOptInfo *index,
+                    IndexClauseSet *rclauseset,
+                    IndexClauseSet *jclauseset,
+                    IndexClauseSet *eclauseset,
+                    List **bitindexpaths,
+                    Relids relids,
+                    List **considered_relids);
+static bool bms_equal_any(Relids relids, List *relids_list);
 static void get_index_paths(PlannerInfo *root, RelOptInfo *rel,
                IndexOptInfo *index, IndexClauseSet *clauses,
                List **bitindexpaths);
        /*
         * Identify the join clauses that can match the index.  For the moment
         * we keep them separate from the restriction clauses.  Note that this
-        * finds only "loose" join clauses that have not been merged into
+        * step finds only "loose" join clauses that have not been merged into
         * EquivalenceClasses.  Also, collect join OR clauses for later.
         */
        MemSet(&jclauseset, 0, sizeof(jclauseset));
        match_eclass_clauses_to_index(root, index, &eclauseset);
 
        /*
-        * If we found any plain or eclass join clauses, decide what to do
-        * with 'em.
+        * If we found any plain or eclass join clauses, build parameterized
+        * index paths using them.
         */
        if (jclauseset.nonempty || eclauseset.nonempty)
            consider_index_join_clauses(root, rel, index,
     * is to generate one such path for each distinct parameterization seen
     * among the available bitmap index paths.  This may look pretty
     * expensive, but usually there won't be very many distinct
-    * parameterizations.
+    * parameterizations.  (This logic is quite similar to that in
+    * consider_index_join_clauses, but we're working with whole paths not
+    * individual clauses.)
     */
    if (bitjoinpaths != NIL)
    {
        {
            Path       *path = (Path *) lfirst(lc);
            Relids      required_outer;
-           bool        found = false;
-           ListCell   *lco;
 
            required_outer = get_bitmap_tree_required_outer(path);
            path_outer = lappend(path_outer, required_outer);
-
-           /* Have we already seen this param set? */
-           foreach(lco, all_path_outers)
-           {
-               Relids      existing_outers = (Relids) lfirst(lco);
-
-               if (bms_equal(existing_outers, required_outer))
-               {
-                   found = true;
-                   break;
-               }
-           }
-           if (!found)
-           {
-               /* No, so add it to all_path_outers */
+           if (!bms_equal_any(required_outer, all_path_outers))
                all_path_outers = lappend(all_path_outers, required_outer);
-           }
        }
 
        /* Now, for each distinct parameterization set ... */
  * 'jclauseset' is the collection of indexable simple join clauses
  * 'eclauseset' is the collection of indexable clauses from EquivalenceClasses
  * '*bitindexpaths' is the list to add bitmap paths to
- *
- * Note: this changes the clause lists contained in the passed clausesets,
- * but we don't care since the caller is done with them.
  */
 static void
 consider_index_join_clauses(PlannerInfo *root, RelOptInfo *rel,
                            IndexClauseSet *eclauseset,
                            List **bitindexpaths)
 {
-   IndexClauseSet clauseset;
-   int         last_eclass_col;
+   List       *considered_relids = NIL;
    int         indexcol;
 
    /*
-    * We can always include any restriction clauses in the index clauses.
-    * However, it's not obvious which subsets of the join clauses are worth
-    * generating paths from, and it's unlikely that considering every
-    * possible subset is worth the cycles.  Our current heuristic is based on
-    * the index columns, with the idea that later index columns are less
-    * useful than earlier ones; therefore it's unlikely to be worth trying
-    * combinations that would remove a clause from an earlier index column
-    * while adding one to a later column.  Also, we know that all the eclass
-    * clauses for a particular column are redundant, so we should use only
-    * one of them.  However, eclass clauses will always represent equality
-    * which is the strongest type of index constraint, so those are
-    * high-value and we should try every available combination when we have
-    * eclass clauses for more than one column.  Furthermore, it's unlikely to
-    * be useful to combine an eclass clause with non-eclass clauses for the
-    * same index column.  These considerations lead to the following
-    * heuristics:
-    *
-    * First, start with the restriction clauses, and add on all simple join
-    * clauses for column 1.  If there are any such join clauses, generate
-    * paths with this collection of clauses.  Then, if there are eclass
-    * clauses for column 1, generate paths with each one of them replacing
-    * any other clauses we have for column 1.
+    * The strategy here is to identify every potentially useful set of outer
+    * rels that can provide indexable join clauses.  For each such set,
+    * select all the join clauses available from those outer rels, add on all
+    * the indexable restriction clauses, and generate plain and/or bitmap
+    * index paths for that set of clauses.  This is based on the assumption
+    * that it's always better to apply a clause as an indexqual than as a
+    * filter (qpqual); which is where an available clause would end up being
+    * applied if we omit it from the indexquals.
     *
-    * Next, add on all simple join clauses for column 2.  If there are any
-    * such join clauses, generate paths with this collection.  If there are
-    * eclass clauses for columns 1 or 2, generate paths with each such clause
-    * replacing other clauses for its index column, including cases where we
-    * use restriction or simple join clauses for one column and an eclass
-    * clause for the other.
+    * This looks expensive, but in practical cases there won't be very many
+    * distinct sets of outer rels to consider.
     *
-    * Repeat for each additional index column.
+    * For simplicity in selecting relevant clauses, we represent each set of
+    * outer rels as a maximum set of clause_relids --- that is, the indexed
+    * relation itself is also included in the relids set.  considered_relids
+    * lists all relids sets we've already tried.
     */
+   for (indexcol = 0; indexcol < index->ncolumns; indexcol++)
+   {
+       /* Consider each applicable simple join clause */
+       consider_index_join_outer_rels(root, rel, index,
+                                      rclauseset, jclauseset, eclauseset,
+                                      bitindexpaths,
+                                      jclauseset->indexclauses[indexcol],
+                                      &considered_relids);
+       /* Consider each applicable eclass join clause */
+       consider_index_join_outer_rels(root, rel, index,
+                                      rclauseset, jclauseset, eclauseset,
+                                      bitindexpaths,
+                                      eclauseset->indexclauses[indexcol],
+                                      &considered_relids);
+   }
+}
 
-   /* Set up working set with just the restriction clauses */
-   memcpy(&clauseset, rclauseset, sizeof(clauseset));
-   /* Even if it's empty right now, it won't be by the time we use it */
-   clauseset.nonempty = true;
+/*
+ * consider_index_join_outer_rels
+ *   Generate parameterized paths based on clause relids in the clause list.
+ *
+ * Workhorse for consider_index_join_clauses; see notes therein for rationale.
+ *
+ * 'rel', 'index', 'rclauseset', 'jclauseset', 'eclauseset', and
+ *     'bitindexpaths' as above
+ * 'indexjoinclauses' is a list of RestrictInfos for join clauses
+ * '*considered_relids' is a list of all relids sets already considered
+ */
+static void
+consider_index_join_outer_rels(PlannerInfo *root, RelOptInfo *rel,
+                              IndexOptInfo *index,
+                              IndexClauseSet *rclauseset,
+                              IndexClauseSet *jclauseset,
+                              IndexClauseSet *eclauseset,
+                              List **bitindexpaths,
+                              List *indexjoinclauses,
+                              List **considered_relids)
+{
+   ListCell   *lc;
 
-   last_eclass_col = -1;
-   for (indexcol = 0; indexcol < index->ncolumns; indexcol++)
+   /* Examine relids of each joinclause in the given list */
+   foreach(lc, indexjoinclauses)
    {
-       /*
-        * If we don't have either simple join clauses or eclass clauses for
-        * this column, no new paths can be created in this iteration.
-        */
-       if (jclauseset->indexclauses[indexcol] == NIL &&
-           eclauseset->indexclauses[indexcol] == NIL)
+       RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+       Relids      clause_relids = rinfo->clause_relids;
+       ListCell   *lc2;
+
+       /* If we already tried its relids set, no need to do so again */
+       if (bms_equal_any(clause_relids, *considered_relids))
            continue;
 
-       /* Add any simple join clauses to the working set */
-       clauseset.indexclauses[indexcol] =
-           list_concat(clauseset.indexclauses[indexcol],
-                       jclauseset->indexclauses[indexcol]);
+       /*
+        * Generate the union of this clause's relids set with each
+        * previously-tried set.  This ensures we try this clause along with
+        * every interesting subset of previous clauses.
+        *
+        * Note: get_join_index_paths adds entries to *considered_relids, but
+        * it prepends them to the list, so that we won't visit new entries
+        * during the inner foreach loop.  No real harm would be done if we
+        * did, since the subset check would reject them; but it would waste
+        * some cycles.
+        */
+       foreach(lc2, *considered_relids)
+       {
+           Relids  oldrelids = (Relids) lfirst(lc2);
 
-       /* Set recursion depth to reach last col with eclass clauses */
-       if (eclauseset->indexclauses[indexcol] != NIL)
-           last_eclass_col = indexcol;
+           /*
+            * If either is a subset of the other, no new set is possible.
+            * This isn't a complete test for redundancy, but it's easy and
+            * cheap.  get_join_index_paths will check more carefully if we
+            * already generated the same relids set.
+            */
+           if (bms_subset_compare(clause_relids, oldrelids) != BMS_DIFFERENT)
+               continue;
 
-       /* Do we have eclass clauses for any column now under consideration? */
-       if (last_eclass_col >= 0)
-       {
-           /* Yes, so recursively generate all eclass clause combinations */
-           expand_eclass_clause_combinations(root, rel, index,
-                                             0, last_eclass_col,
-                                             &clauseset, eclauseset,
-                                             bitindexpaths);
-       }
-       else
-       {
-           /* No, consider the newly-enlarged set of simple join clauses */
-           get_index_paths(root, rel, index, &clauseset, bitindexpaths);
+           /* OK, try the union set */
+           get_join_index_paths(root, rel, index,
+                                rclauseset, jclauseset, eclauseset,
+                                bitindexpaths,
+                                bms_union(clause_relids, oldrelids),
+                                considered_relids);
        }
+
+       /* Also try this set of relids by itself */
+       get_join_index_paths(root, rel, index,
+                            rclauseset, jclauseset, eclauseset,
+                            bitindexpaths,
+                            clause_relids,
+                            considered_relids);
    }
 }
 
 /*
- * expand_eclass_clause_combinations
- *     Generate all combinations of eclass join clauses for first N columns,
- *     and construct parameterized index paths for each combination.
+ * get_join_index_paths
+ *   Generate index paths using clauses from the specified outer relations.
+ *   In addition to generating paths, relids is added to *considered_relids
+ *   if not already present.
  *
  * Workhorse for consider_index_join_clauses; see notes therein for rationale.
- * It's convenient to use recursion to implement the enumeration, since we
- * can have at most INDEX_MAX_KEYS recursion levels.
  *
- * 'rel', 'index', 'eclauseset', 'bitindexpaths' as above
- * 'thiscol' is the current index column number/recursion level
- * 'lastcol' is the last index column we should consider eclass clauses for
- * 'clauseset' is the current collection of indexable clauses
+ * 'rel', 'index', 'rclauseset', 'jclauseset', 'eclauseset',
+ *     'bitindexpaths', 'considered_relids' as above
+ * 'relids' is the current set of relids to consider (the target rel plus
+ *     one or more outer rels)
  */
 static void
-expand_eclass_clause_combinations(PlannerInfo *root, RelOptInfo *rel,
-                                 IndexOptInfo *index,
-                                 int thiscol, int lastcol,
-                                 IndexClauseSet *clauseset,
-                                 IndexClauseSet *eclauseset,
-                                 List **bitindexpaths)
+get_join_index_paths(PlannerInfo *root, RelOptInfo *rel,
+                    IndexOptInfo *index,
+                    IndexClauseSet *rclauseset,
+                    IndexClauseSet *jclauseset,
+                    IndexClauseSet *eclauseset,
+                    List **bitindexpaths,
+                    Relids relids,
+                    List **considered_relids)
 {
-   List       *save_clauses;
-   ListCell   *lc;
+   IndexClauseSet clauseset;
+   int         indexcol;
 
-   /* If past last eclass column, end the recursion and generate paths */
-   if (thiscol > lastcol)
-   {
-       get_index_paths(root, rel, index, clauseset, bitindexpaths);
+   /* If we already considered this relids set, don't repeat the work */
+   if (bms_equal_any(relids, *considered_relids))
        return;
-   }
 
-   /* If no eclass clauses to consider for this column, just recurse */
-   if (eclauseset->indexclauses[thiscol] == NIL)
+   /* Identify indexclauses usable with this relids set */
+   MemSet(&clauseset, 0, sizeof(clauseset));
+
+   for (indexcol = 0; indexcol < index->ncolumns; indexcol++)
    {
-       Assert(thiscol < lastcol);
-       expand_eclass_clause_combinations(root, rel, index,
-                                         thiscol + 1, lastcol,
-                                         clauseset, eclauseset,
-                                         bitindexpaths);
-       return;
-   }
+       ListCell   *lc;
 
-   /* We'll momentarily save and restore the list of non-eclass clauses */
-   save_clauses = clauseset->indexclauses[thiscol];
+       /* First find applicable simple join clauses */
+       foreach(lc, jclauseset->indexclauses[indexcol])
+       {
+           RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
 
-   /* If we have non-eclass clauses for this column, first try with those */
-   if (save_clauses)
-       expand_eclass_clause_combinations(root, rel, index,
-                                         thiscol + 1, lastcol,
-                                         clauseset, eclauseset,
-                                         bitindexpaths);
+           if (bms_is_subset(rinfo->clause_relids, relids))
+               clauseset.indexclauses[indexcol] =
+                   lappend(clauseset.indexclauses[indexcol], rinfo);
+       }
 
-   /* For each eclass clause alternative ... */
-   foreach(lc, eclauseset->indexclauses[thiscol])
-   {
-       RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+       /*
+        * Add applicable eclass join clauses.  The clauses generated for each
+        * column are redundant (cf generate_implied_equalities_for_indexcol),
+        * so we need at most one.  This is the only exception to the general
+        * rule of using all available index clauses.
+        */
+       foreach(lc, eclauseset->indexclauses[indexcol])
+       {
+           RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+
+           if (bms_is_subset(rinfo->clause_relids, relids))
+           {
+               clauseset.indexclauses[indexcol] =
+                   lappend(clauseset.indexclauses[indexcol], rinfo);
+               break;
+           }
+       }
 
-       /* Replace any existing clauses with the eclass clause */
-       clauseset->indexclauses[thiscol] = list_make1(rinfo);
+       /* Add restriction clauses (this is nondestructive to rclauseset) */
+       clauseset.indexclauses[indexcol] =
+           list_concat(clauseset.indexclauses[indexcol],
+                       rclauseset->indexclauses[indexcol]);
 
-       /* Recurse to advance to next column */
-       expand_eclass_clause_combinations(root, rel, index,
-                                         thiscol + 1, lastcol,
-                                         clauseset, eclauseset,
-                                         bitindexpaths);
+       if (clauseset.indexclauses[indexcol] != NIL)
+           clauseset.nonempty = true;
    }
 
-   /* Restore previous list contents */
-   clauseset->indexclauses[thiscol] = save_clauses;
+   /* We should have found something, else caller passed silly relids */
+   Assert(clauseset.nonempty);
+
+   /* Build index path(s) using the collected set of clauses */
+   get_index_paths(root, rel, index, &clauseset, bitindexpaths);
+
+   /*
+    * Remember we considered paths for this set of relids.  We use lcons not
+    * lappend to avoid confusing the loop in consider_index_join_outer_rels.
+    */
+   *considered_relids = lcons(relids, *considered_relids);
+}
+
+/*
+ * bms_equal_any
+ *     True if relids is bms_equal to any member of relids_list
+ *
+ * Perhaps this should be in bitmapset.c someday.
+ */
+static bool
+bms_equal_any(Relids relids, List *relids_list)
+{
+   ListCell   *lc;
+
+   foreach(lc, relids_list)
+   {
+       if (bms_equal(relids, (Relids) lfirst(lc)))
+           return true;
+   }
+   return false;
 }