diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 738bb30848..8b6a147cf7 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -210,6 +210,8 @@ make_one_rel(PlannerInfo *root, List *joinlist)
 	}
 	root->total_table_pages = total_pages;
 
+	root->eq_index = create_eclass_index(root, EQUIVALANCE_IDX_MULTI_MEMBER);
+
 	/*
 	 * Generate access paths for each base rel.
 	 */
@@ -220,6 +222,9 @@ make_one_rel(PlannerInfo *root, List *joinlist)
 	 */
 	rel = make_rel_from_joinlist(root, joinlist);
 
+	free_eclass_index(root->eq_index);
+	root->eq_index = NULL;
+
 	/*
 	 * The result should join all and only the query's base rels.
 	 */
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index b22b36ec0e..54a408c7ae 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -1114,6 +1114,60 @@ generate_join_implied_equalities_for_ecs(PlannerInfo *root,
 		nominal_join_relids = join_relids;
 	}
 
+	if (root->eq_index != NULL)
+	{
+		Bitmapset  *inner_ecs = NULL;
+		Bitmapset  *join_ecs = NULL;
+		Bitmapset  *matching_ecs;
+		int			i;
+
+		Assert((root->eq_index->ei_flags & EQUIVALANCE_IDX_MULTI_MEMBER));
+
+		i = -1;
+		while ((i = bms_next_member(inner_relids, i)) > 0)
+			inner_ecs = bms_add_members(inner_ecs, root->eq_index->ei_index[i]);
+
+		i = -1;
+		while ((i = bms_next_member(join_relids, i)) > 0)
+			join_ecs = bms_add_members(join_ecs, root->eq_index->ei_index[i]);
+
+		/* Determine all eclasses in common between inner rels and join rels */
+		matching_ecs = bms_int_members(inner_ecs, join_ecs);
+
+		i = -1;
+		while ((i = bms_next_member(matching_ecs, i)) >= 0)
+		{
+			EquivalenceClass *ec = root->eq_index->ei_classes[i];
+			List	   *sublist = NIL;
+
+			/* ECs containing consts do not need any further enforcement */
+			if (ec->ec_has_const)
+				continue;
+
+			/* Ensure the class contains members for any of nominal_join_relids */
+			Assert(bms_overlap(ec->ec_relids, nominal_join_relids));
+
+			if (!ec->ec_broken)
+				sublist = generate_join_implied_equalities_normal(root,
+																  ec,
+																  join_relids,
+																  outer_relids,
+																  inner_relids);
+
+			/* Recover if we failed to generate required derived clauses */
+			if (ec->ec_broken)
+				sublist = generate_join_implied_equalities_broken(root,
+																  ec,
+																  nominal_join_relids,
+																  outer_relids,
+																  nominal_inner_relids,
+																  inner_rel);
+
+			result = list_concat(result, sublist);
+		}
+		return result;
+	}
+
 	foreach(lc, eclasses)
 	{
 		EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc);
@@ -2026,6 +2080,8 @@ exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2)
  * we ignore that fine point here.)  This is much like exprs_known_equal,
  * except that we insist on the comparison operator matching the eclass, so
  * that the result is definite not approximate.
+ *
+ * An eq_index without volatile classes must already exist on 'root'.
  */
 EquivalenceClass *
 match_eclasses_to_foreign_key_col(PlannerInfo *root,
@@ -2038,23 +2094,35 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
 	AttrNumber	var2attno = fkinfo->confkey[colno];
 	Oid			eqop = fkinfo->conpfeqop[colno];
 	List	   *opfamilies = NIL;	/* compute only if needed */
-	ListCell   *lc1;
+	EquivalenceClassIndex *eq_index;
+	Bitmapset  *matching_ecs;
+	int			i;
 
-	foreach(lc1, root->eq_classes)
+	Assert(root->eq_index != NULL);
+	Assert((root->eq_index->ei_flags & EQUIVALANCE_IDX_NON_VOLATILE) != 0);
+
+	eq_index = root->eq_index;
+
+	/* Determine which eclasses contain members for both of the relations */
+	matching_ecs = bms_intersect(eq_index->ei_index[var1varno],
+								 eq_index->ei_index[var2varno]);
+
+	i = -1;
+	while ((i = bms_next_member(matching_ecs, i)) >= 0)
 	{
-		EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc1);
+		EquivalenceClass *ec = eq_index->ei_classes[i];
 		bool		item1member = false;
 		bool		item2member = false;
-		ListCell   *lc2;
+		ListCell   *lc;
+
+		/* the eclass index shouldn't have indexed these */
+		Assert(!ec->ec_has_volatile);
 
-		/* Never match to a volatile EC */
-		if (ec->ec_has_volatile)
-			continue;
 		/* Note: it seems okay to match to "broken" eclasses here */
 
-		foreach(lc2, ec->ec_members)
+		foreach(lc, ec->ec_members)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
 			Var		   *var;
 
 			if (em->em_is_child)
@@ -2346,6 +2414,23 @@ have_relevant_eclass_joinclause(PlannerInfo *root,
 {
 	ListCell   *lc1;
 
+	if (root->eq_index != NULL)
+	{
+		Bitmapset *rel1_ecs = NULL;
+		Bitmapset *rel2_ecs = NULL;
+
+		int i = -1;
+		while ((i = bms_next_member(rel1->relids, i)) > 0)
+			rel1_ecs = bms_add_members(rel1_ecs, root->eq_index->ei_index[i]);
+
+		i = -1;
+		while ((i = bms_next_member(rel2->relids, i)) > 0)
+			rel2_ecs = bms_add_members(rel2_ecs, root->eq_index->ei_index[i]);
+
+		/* return true if there are any eclasses in common between the two relations */
+		return bms_overlap(rel1_ecs, rel2_ecs);
+	}
+
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc1);
@@ -2511,3 +2596,76 @@ is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist)
 
 	return false;
 }
+
+EquivalenceClassIndex *
+create_eclass_index(PlannerInfo *root, int flags)
+{
+	EquivalenceClassIndex *ec_index = makeNode(EquivalenceClassIndex);
+	EquivalenceClass	  **ei_classes;
+	Bitmapset			  **ei_index;
+	ListCell *lc;
+	int i;
+
+	ec_index->ei_classes = ei_classes = palloc(sizeof(EquivalenceClass *) *
+		list_length(root->eq_classes));
+	ec_index->ei_index = ei_index = palloc0(sizeof(Bitmapset *) *
+		root->simple_rel_array_size);
+	ec_index->ei_flags = flags;
+
+	/*
+	 * Index the eclass list so that we can quickly identify eclasses
+	 * belonging to a given relation.  First we build an array to store
+	 * each eclass, this allows us to quickly lookup the eclass by array
+	 * index.  We then build an index using a Bitmapset for each relation to
+	 * mark which eclass array element contains a member for that relation.
+	 */
+	i = 0;
+	foreach(lc, root->eq_classes)
+		ei_classes[i++] = lfirst(lc);
+
+	/*
+	 * We could build the indexes in the loop above, but looping backwards
+	 * allows the Bitmapset to be allocated to its final size on the first
+	 * allocation.  Doing this forward could cause small incremental
+	 * allocations which can be slower.
+	 */
+	for (i--; i >= 0; i--)
+	{
+		int relid = -1;
+
+		while ((relid = bms_next_member(ei_classes[i]->ec_relids, relid)) > 0)
+		{
+			EquivalenceClass  *ec = ei_classes[i];
+
+			/* Don't index classes with a const if flags mention not to. */
+			if (ec->ec_has_const && (flags & EQUIVALANCE_IDX_NON_CONST) != 0)
+				continue;
+
+			/* Skip volatile classes when not required in the index */
+			if (ec->ec_has_volatile &&
+				(flags & EQUIVALANCE_IDX_NON_VOLATILE) != 0)
+				continue;
+
+			if (list_length(ec->ec_members) <= 1 &&
+				(flags & EQUIVALANCE_IDX_MULTI_MEMBER) != 0)
+				continue;
+
+			Assert(relid < root->simple_rel_array_size);
+
+			/* mark this eclass as having a member for relid */
+			ei_index[relid] = bms_add_member(ei_index[relid], i);
+		}
+	}
+
+	return ec_index;
+}
+
+void
+free_eclass_index(EquivalenceClassIndex *eci)
+{
+	Assert(IsA(eci, EquivalenceClassIndex));
+
+	pfree(eci->ei_classes);
+	pfree(eci->ei_index);
+}
+
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index 01335db511..0fe7b1b28e 100644
--- a/src/backend/optimizer/plan/initsplan.c
+++ b/src/backend/optimizer/plan/initsplan.c
@@ -2441,6 +2441,9 @@ match_foreign_keys_to_quals(PlannerInfo *root)
 	List	   *newlist = NIL;
 	ListCell   *lc;
 
+
+	root->eq_index = create_eclass_index(root, EQUIVALANCE_IDX_NON_VOLATILE);
+
 	foreach(lc, root->fkey_list)
 	{
 		ForeignKeyOptInfo *fkinfo = (ForeignKeyOptInfo *) lfirst(lc);
@@ -2586,6 +2589,10 @@ match_foreign_keys_to_quals(PlannerInfo *root)
 	}
 	/* Replace fkey_list, thereby discarding any useless entries */
 	root->fkey_list = newlist;
+
+	/* clean up the eclass index */
+	free_eclass_index(root->eq_index);
+	root->eq_index = NULL;
 }
 
 
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index cac6ff0eda..a22a3fb5fc 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -259,6 +259,7 @@ typedef enum NodeTag
 	/* these aren't subclasses of Path: */
 	T_EquivalenceClass,
 	T_EquivalenceMember,
+	T_EquivalenceClassIndex,
 	T_PathKey,
 	T_PathTarget,
 	T_RestrictInfo,
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 6fd24203dd..86f11ee89c 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -162,6 +162,7 @@ typedef struct PlannerGlobal
  *----------
  */
 struct AppendRelInfo;
+struct EquivalenceClassIndex;
 
 typedef struct PlannerInfo
 {
@@ -257,6 +258,8 @@ typedef struct PlannerInfo
 
 	List	   *eq_classes;		/* list of active EquivalenceClasses */
 
+	struct EquivalenceClassIndex *eq_index;	/* Temporary eclass index */
+
 	List	   *canon_pathkeys; /* list of "canonical" PathKeys */
 
 	List	   *left_join_clauses;	/* list of RestrictInfos for mergejoinable
@@ -923,6 +926,22 @@ typedef struct EquivalenceClass
 	struct EquivalenceClass *ec_merged; /* set if merged into another EC */
 } EquivalenceClass;
 
+typedef struct EquivalenceClassIndex
+{
+	NodeTag		type;
+
+	EquivalenceClass	  **ei_classes;	/* an array of EquivalenceClasses */
+	Bitmapset			  **ei_index; /* an array, indexed by relid contining
+									   * a bitmapset of each ei_classes item
+									   * which has a member belonging to this
+									   * relation */
+	int						ei_flags;
+} EquivalenceClassIndex;
+
+#define EQUIVALANCE_IDX_NON_CONST		1
+#define EQUIVALANCE_IDX_NON_VOLATILE	2
+#define EQUIVALANCE_IDX_MULTI_MEMBER	4
+
 /*
  * If an EC contains a const and isn't below-outer-join, any PathKey depending
  * on it must be redundant, since there's only one possible value of the key.
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index cafde307ad..ac6a509776 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -175,6 +175,9 @@ extern bool eclass_useful_for_merging(PlannerInfo *root,
 						  EquivalenceClass *eclass,
 						  RelOptInfo *rel);
 extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist);
+extern EquivalenceClassIndex *create_eclass_index(PlannerInfo *root,
+					int flags);
+extern void free_eclass_index(EquivalenceClassIndex *eci);
 
 /*
  * pathkeys.c
