diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 61b5b119b0..3c37665d18 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -31,8 +31,8 @@
 #include "optimizer/restrictinfo.h"
 #include "utils/lsyscache.h"
 
-
-static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
+static EquivalenceMember *add_eq_member(PlannerInfo *root,
+			  EquivalenceClass *ec, int ec_index,
 			  Expr *expr, Relids relids, Relids nullable_relids,
 			  bool is_child, Oid datatype);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
@@ -64,6 +64,10 @@ static bool reconsider_outer_join_clause(PlannerInfo *root,
 							 bool outer_on_left);
 static bool reconsider_full_join_clause(PlannerInfo *root,
 							RestrictInfo *rinfo);
+static Bitmapset *get_eclass_indexes_for_relids(PlannerInfo *root,
+							  Relids relids);
+static Bitmapset *get_common_eclass_indexes(PlannerInfo *root, Relids relids1,
+						  Relids relids2);
 
 
 /*
@@ -133,6 +137,9 @@ process_equivalence(PlannerInfo *root,
 	EquivalenceMember *em1,
 			   *em2;
 	ListCell   *lc1;
+	int			ec1idx,
+				ec2idx,
+				cur_ec_idx;
 
 	/* Should not already be marked as having generated an eclass */
 	Assert(restrictinfo->left_ec == NULL);
@@ -150,6 +157,8 @@ process_equivalence(PlannerInfo *root,
 	item2 = (Expr *) get_rightop(clause);
 	item1_relids = restrictinfo->left_relids;
 	item2_relids = restrictinfo->right_relids;
+	ec1idx = -1;
+	ec2idx = -1;
 
 	/*
 	 * Ensure both input expressions expose the desired collation (their types
@@ -254,11 +263,18 @@ process_equivalence(PlannerInfo *root,
 	 */
 	ec1 = ec2 = NULL;
 	em1 = em2 = NULL;
+	cur_ec_idx = -1;
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
 		ListCell   *lc2;
 
+		cur_ec_idx++;
+
+		/* skip deleted ECs */
+		if (cur_ec->ec_members == NIL)
+			continue;
+
 		/* Never match to a volatile EC */
 		if (cur_ec->ec_has_volatile)
 			continue;
@@ -298,6 +314,7 @@ process_equivalence(PlannerInfo *root,
 			{
 				ec1 = cur_ec;
 				em1 = cur_em;
+				ec1idx = cur_ec_idx;
 				if (ec2)
 					break;
 			}
@@ -308,6 +325,7 @@ process_equivalence(PlannerInfo *root,
 			{
 				ec2 = cur_ec;
 				em2 = cur_em;
+				ec2idx = cur_ec_idx;
 				if (ec1)
 					break;
 			}
@@ -349,10 +367,11 @@ process_equivalence(PlannerInfo *root,
 
 		/*
 		 * We add ec2's items to ec1, then set ec2's ec_merged link to point
-		 * to ec1 and remove ec2 from the eq_classes list.  We cannot simply
-		 * delete ec2 because that could leave dangling pointers in existing
-		 * PathKeys.  We leave it behind with a link so that the merged EC can
-		 * be found.
+		 * to ec1.  We cannot simply delete ec2 because that could leave
+		 * dangling pointers in existing PathKeys.  We leave it behind with a
+		 * link so that the merged EC can be found.  We don't remove the EC
+		 * from the eq_classes list either, instead, we nullify the ec_members
+		 * list to mark it deleted.
 		 */
 		ec1->ec_members = list_concat(ec1->ec_members, ec2->ec_members);
 		ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
@@ -366,8 +385,12 @@ process_equivalence(PlannerInfo *root,
 		ec1->ec_max_security = Max(ec1->ec_max_security,
 								   ec2->ec_max_security);
 		ec2->ec_merged = ec1;
-		root->eq_classes = list_delete_ptr(root->eq_classes, ec2);
-		/* just to avoid debugging confusion w/ dangling pointers: */
+
+		/*
+		 * Nullify ec_members to mark the EC as deleted.  Also set some other
+		 * fields to null just to avoid debugging confusion w/ dangling
+		 * pointers:
+		 */
 		ec2->ec_members = NIL;
 		ec2->ec_sources = NIL;
 		ec2->ec_derives = NIL;
@@ -388,8 +411,8 @@ process_equivalence(PlannerInfo *root,
 	else if (ec1)
 	{
 		/* Case 3: add item2 to ec1 */
-		em2 = add_eq_member(ec1, item2, item2_relids, item2_nullable_relids,
-							false, item2_type);
+		em2 = add_eq_member(root, ec1, ec1idx, item2, item2_relids,
+							item2_nullable_relids, false, item2_type);
 		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_below_outer_join |= below_outer_join;
 		ec1->ec_min_security = Min(ec1->ec_min_security,
@@ -406,8 +429,8 @@ process_equivalence(PlannerInfo *root,
 	else if (ec2)
 	{
 		/* Case 3: add item1 to ec2 */
-		em1 = add_eq_member(ec2, item1, item1_relids, item1_nullable_relids,
-							false, item1_type);
+		em1 = add_eq_member(root, ec2, ec2idx, item1, item1_relids,
+							item1_nullable_relids, false, item1_type);
 		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
 		ec2->ec_below_outer_join |= below_outer_join;
 		ec2->ec_min_security = Min(ec2->ec_min_security,
@@ -440,10 +463,12 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_min_security = restrictinfo->security_level;
 		ec->ec_max_security = restrictinfo->security_level;
 		ec->ec_merged = NULL;
-		em1 = add_eq_member(ec, item1, item1_relids, item1_nullable_relids,
-							false, item1_type);
-		em2 = add_eq_member(ec, item2, item2_relids, item2_nullable_relids,
-							false, item2_type);
+		em1 = add_eq_member(root, ec, list_length(root->eq_classes), item1,
+							item1_relids, item1_nullable_relids, false,
+							item1_type);
+		em2 = add_eq_member(root, ec, list_length(root->eq_classes), item2,
+							item2_relids, item2_nullable_relids, false,
+							item2_type);
 
 		root->eq_classes = lappend(root->eq_classes, ec);
 
@@ -539,13 +564,19 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
 
 /*
  * add_eq_member - build a new EquivalenceMember and add it to an EC
+ *
+ * ec_index must be the list index that this ec is, or will be stored in
+ * root's eq_classes.
  */
 static EquivalenceMember *
-add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
-			  Relids nullable_relids, bool is_child, Oid datatype)
+add_eq_member(PlannerInfo *root, EquivalenceClass *ec, int ec_index,
+			  Expr *expr, Relids relids, Relids nullable_relids,
+			  bool is_child, Oid datatype)
 {
 	EquivalenceMember *em = makeNode(EquivalenceMember);
 
+	Assert(ec_index >= 0);
+
 	em->em_expr = expr;
 	em->em_relids = relids;
 	em->em_nullable_relids = nullable_relids;
@@ -568,9 +599,24 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 		ec->ec_has_const = true;
 		/* it can't affect ec_relids */
 	}
-	else if (!is_child)			/* child members don't add to ec_relids */
+	else
 	{
-		ec->ec_relids = bms_add_members(ec->ec_relids, relids);
+		int			i;
+
+		/* Record this eclass in each rel's eclass_indexes set */
+		i = -1;
+		while ((i = bms_next_member(relids, i)) > 0)
+		{
+			RelOptInfo *rel = root->simple_rel_array[i];
+
+			rel->eclass_indexes = bms_add_member(rel->eclass_indexes,
+												 ec_index);
+		}
+
+		if (!is_child)			/* child members don't add to ec_relids */
+		{
+			ec->ec_relids = bms_add_members(ec->ec_relids, relids);
+		}
 	}
 	ec->ec_members = lappend(ec->ec_members, em);
 
@@ -652,6 +698,10 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
 		ListCell   *lc2;
 
+		/* skip deleted ECs */
+		if (cur_ec->ec_members == NIL)
+			continue;
+
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
 		 * reference to the same volatile SortGroupClause.
@@ -720,7 +770,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	if (newec->ec_has_volatile && sortref == 0) /* should not happen */
 		elog(ERROR, "volatile EquivalenceClass has no sortref");
 
-	newem = add_eq_member(newec, copyObject(expr), expr_relids,
+	newem = add_eq_member(root, newec, list_length(root->eq_classes),
+						  copyObject(expr), expr_relids,
 						  nullable_relids, false, opcintype);
 
 	/*
@@ -807,10 +858,9 @@ generate_base_implied_equalities(PlannerInfo *root)
 	{
 		EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc);
 
-		Assert(ec->ec_merged == NULL);	/* else shouldn't be in list */
 		Assert(!ec->ec_broken); /* not yet anyway... */
 
-		/* Single-member ECs won't generate any deductions */
+		/* Zero or single-member ECs won't generate any deductions */
 		if (list_length(ec->ec_members) <= 1)
 			continue;
 
@@ -1096,7 +1146,8 @@ generate_join_implied_equalities_for_ecs(PlannerInfo *root,
 	Relids		inner_relids = inner_rel->relids;
 	Relids		nominal_inner_relids;
 	Relids		nominal_join_relids;
-	ListCell   *lc;
+	Bitmapset  *matching_ecs;
+	int			i;
 
 	/* If inner rel is a child, extra setup work is needed */
 	if (IS_OTHER_REL(inner_rel))
@@ -1114,22 +1165,25 @@ generate_join_implied_equalities_for_ecs(PlannerInfo *root,
 		nominal_join_relids = join_relids;
 	}
 
-	foreach(lc, eclasses)
+	/* Get all eclasses in common between inner_relids and join_relids */
+	matching_ecs = get_common_eclass_indexes(root, inner_relids, join_relids);
+
+	i = -1;
+	while ((i = bms_next_member(matching_ecs, i)) >= 0)
 	{
-		EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc);
+		EquivalenceClass *ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		List	   *sublist = NIL;
 
 		/* ECs containing consts do not need any further enforcement */
 		if (ec->ec_has_const)
 			continue;
 
-		/* Single-member ECs won't generate any deductions */
+		/* Zero or single-member ECs won't generate any deductions */
 		if (list_length(ec->ec_members) <= 1)
 			continue;
 
-		/* We can quickly ignore any that don't overlap the join, too */
-		if (!bms_overlap(ec->ec_relids, nominal_join_relids))
-			continue;
+		/* Sanity check that this eclass overlaps the join */
+		Assert(bms_overlap(ec->ec_relids, nominal_join_relids));
 
 		if (!ec->ec_broken)
 			sublist = generate_join_implied_equalities_normal(root,
@@ -1727,6 +1781,9 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 		bool		match;
 		ListCell   *lc2;
 
+		/* skip deleted ECs */
+		if (cur_ec->ec_members == NIL)
+			continue;
 		/* Ignore EC unless it contains pseudoconstants */
 		if (!cur_ec->ec_has_const)
 			continue;
@@ -1845,6 +1902,9 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 		bool		matchright;
 		ListCell   *lc2;
 
+		/* skip deleted ECs */
+		if (cur_ec->ec_members == NIL)
+			continue;
 		/* Ignore EC unless it contains pseudoconstants */
 		if (!cur_ec->ec_has_const)
 			continue;
@@ -1992,6 +2052,9 @@ exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2)
 		bool		item2member = false;
 		ListCell   *lc2;
 
+		/* skip deleted ECs */
+		if (ec->ec_members == NIL)
+			continue;
 		/* Never match to a volatile EC */
 		if (ec->ec_has_volatile)
 			continue;
@@ -2038,31 +2101,36 @@ 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;
+	Bitmapset  *matching_ecs;
+	int			i;
 
-	foreach(lc1, root->eq_classes)
+	/* Determine which eclasses contain members for both of the relations */
+	matching_ecs = bms_intersect(root->simple_rel_array[var1varno]->eclass_indexes,
+								 root->simple_rel_array[var2varno]->eclass_indexes);
+
+	i = -1;
+	while ((i = bms_next_member(matching_ecs, i)) >= 0)
 	{
-		EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc1);
+		EquivalenceClass *ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		bool		item1member = false;
 		bool		item2member = false;
-		ListCell   *lc2;
+		ListCell   *lc;
+
+		/* skip deleted ECs */
+		if (ec->ec_members == NIL)
+			continue;
 
 		/* Never match to a volatile EC */
 		if (ec->ec_has_volatile)
 			continue;
 		/* Note: it seems okay to match to "broken" eclasses here */
 
-		/*
-		 * If eclass visibly doesn't have members for both rels, there's no
-		 * need to grovel through the members.
-		 */
-		if (!bms_is_member(var1varno, ec->ec_relids) ||
-			!bms_is_member(var2varno, ec->ec_relids))
-			continue;
-
-		foreach(lc2, ec->ec_members)
+		/* Ensure no corruption of eclass_indexes */
+		Assert(bms_is_member(var1varno, ec->ec_relids));
+		Assert(bms_is_member(var2varno, ec->ec_relids));
+		foreach(lc, ec->ec_members)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
 			Var		   *var;
 
 			if (em->em_is_child)
@@ -2119,13 +2187,18 @@ add_child_rel_equivalences(PlannerInfo *root,
 						   RelOptInfo *parent_rel,
 						   RelOptInfo *child_rel)
 {
-	ListCell   *lc1;
+	int			i;
 
-	foreach(lc1, root->eq_classes)
+	i = -1;
+	while ((i = bms_next_member(parent_rel->eclass_indexes, i)) >= 0)
 	{
-		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
+		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		ListCell   *lc2;
 
+		/* skip deleted ECs */
+		if (cur_ec->ec_members == NIL)
+			continue;
+
 		/*
 		 * If this EC contains a volatile expression, then generating child
 		 * EMs would be downright dangerous, so skip it.  We rely on a
@@ -2135,12 +2208,11 @@ add_child_rel_equivalences(PlannerInfo *root,
 			continue;
 
 		/*
-		 * No point in searching if parent rel not mentioned in eclass; but we
-		 * can't tell that for sure if parent rel is itself a child.
+		 * Sanity check that eclass_indexes only contains indexes of ECs for
+		 * this parent_rel.  If it's not a baserel then ec_relids are not set.
 		 */
-		if (parent_rel->reloptkind == RELOPT_BASEREL &&
-			!bms_is_subset(parent_rel->relids, cur_ec->ec_relids))
-			continue;
+		Assert(parent_rel->reloptkind != RELOPT_BASEREL ||
+			   bms_is_subset(parent_rel->relids, cur_ec->ec_relids));
 
 		foreach(lc2, cur_ec->ec_members)
 		{
@@ -2185,7 +2257,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 														  child_rel->relids);
 				}
 
-				(void) add_eq_member(cur_ec, child_expr,
+				(void) add_eq_member(root, cur_ec, i, child_expr,
 									 new_relids, new_nullable_relids,
 									 true, cur_em->em_datatype);
 			}
@@ -2227,7 +2299,7 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 	List	   *result = NIL;
 	bool		is_child_rel = (rel->reloptkind == RELOPT_OTHER_MEMBER_REL);
 	Relids		parent_relids;
-	ListCell   *lc1;
+	int			i;
 
 	/* Indexes are available only on base or "other" member relations. */
 	Assert(IS_SIMPLE_REL(rel));
@@ -2238,26 +2310,26 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 	else
 		parent_relids = NULL;	/* not used, but keep compiler quiet */
 
-	foreach(lc1, root->eq_classes)
+	i = -1;
+	while ((i = bms_next_member(rel->eclass_indexes, i)) >= 0)
 	{
-		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
+		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		EquivalenceMember *cur_em;
-		ListCell   *lc2;
+		ListCell   *lc;
 
 		/*
-		 * Won't generate joinclauses if const or single-member (the latter
-		 * test covers the volatile case too)
+		 * Won't generate joinclauses if const, zero or single-member (the
+		 * latter test covers the volatile case too)
 		 */
 		if (cur_ec->ec_has_const || list_length(cur_ec->ec_members) <= 1)
 			continue;
 
 		/*
-		 * No point in searching if rel not mentioned in eclass (but we can't
-		 * tell that for a child rel).
+		 * Ensure that these relids are mentioned in the eclass.  Child rels
+		 * won't be mentioned in ec_relids, so skip the check for those
 		 */
-		if (!is_child_rel &&
-			!bms_is_subset(rel->relids, cur_ec->ec_relids))
-			continue;
+		Assert(is_child_rel || bms_is_subset(rel->relids, cur_ec->ec_relids));
+
 
 		/*
 		 * Scan members, looking for a match to the target column.  Note that
@@ -2270,9 +2342,9 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 		 * match.  See also get_eclass_for_sort_expr.)
 		 */
 		cur_em = NULL;
-		foreach(lc2, cur_ec->ec_members)
+		foreach(lc, cur_ec->ec_members)
 		{
-			cur_em = (EquivalenceMember *) lfirst(lc2);
+			cur_em = (EquivalenceMember *) lfirst(lc);
 			if (bms_equal(cur_em->em_relids, rel->relids) &&
 				callback(root, rel, cur_ec, cur_em, callback_arg))
 				break;
@@ -2286,9 +2358,9 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 		 * Found our match.  Scan the other EC members and attempt to generate
 		 * joinclauses.
 		 */
-		foreach(lc2, cur_ec->ec_members)
+		foreach(lc, cur_ec->ec_members)
 		{
-			EquivalenceMember *other_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *other_em = (EquivalenceMember *) lfirst(lc);
 			Oid			eq_op;
 			RestrictInfo *rinfo;
 
@@ -2352,15 +2424,20 @@ bool
 have_relevant_eclass_joinclause(PlannerInfo *root,
 								RelOptInfo *rel1, RelOptInfo *rel2)
 {
-	ListCell   *lc1;
+	Bitmapset  *matching_ecs;
+	int			i;
 
-	foreach(lc1, root->eq_classes)
+	/* Get all eclasses in common between rel1 and rel2 */
+	matching_ecs = get_common_eclass_indexes(root, rel1->relids, rel2->relids);
+
+	i = -1;
+	while ((i = bms_next_member(matching_ecs, i)) >= 0)
 	{
-		EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc1);
+		EquivalenceClass *ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 
 		/*
-		 * Won't generate joinclauses if single-member (this test covers the
-		 * volatile case too)
+		 * Won't generate joinclauses for zero or single-member ECs (this test
+		 * covers the volatile case too)
 		 */
 		if (list_length(ec->ec_members) <= 1)
 			continue;
@@ -2384,10 +2461,16 @@ have_relevant_eclass_joinclause(PlannerInfo *root,
 		 * b.y and a.x = 42", it is worth considering a join between a and b,
 		 * since the join result is likely to be small even though it'll end
 		 * up being an unqualified nestloop.
+		 *
+		 * Since matching_ecs only contains eclasses common to both relations
+		 * and since we skipped zero and single member eclasses above, we can
+		 * simply return true.
 		 */
-		if (bms_overlap(rel1->relids, ec->ec_relids) &&
-			bms_overlap(rel2->relids, ec->ec_relids))
-			return true;
+
+		Assert(bms_overlap(rel1->relids, ec->ec_relids));
+		Assert(bms_overlap(rel2->relids, ec->ec_relids));
+
+		return true;
 	}
 
 	return false;
@@ -2405,25 +2488,33 @@ have_relevant_eclass_joinclause(PlannerInfo *root,
 bool
 has_relevant_eclass_joinclause(PlannerInfo *root, RelOptInfo *rel1)
 {
-	ListCell   *lc1;
+	Bitmapset  *matched_ecs;
+	int			i;
 
-	foreach(lc1, root->eq_classes)
+	if (IS_SIMPLE_REL(rel1))
+		matched_ecs = rel1->eclass_indexes;
+	else
+		matched_ecs = get_eclass_indexes_for_relids(root, rel1->relids);
+
+	i = -1;
+	while ((i = bms_next_member(matched_ecs, i)) >= 0)
 	{
-		EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc1);
+		EquivalenceClass *ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 
 		/*
-		 * Won't generate joinclauses if single-member (this test covers the
-		 * volatile case too)
+		 * Won't generate joinclauses for zero or single-member ECs (this test
+		 * covers the volatile case too)
 		 */
 		if (list_length(ec->ec_members) <= 1)
 			continue;
 
+		Assert(bms_overlap(rel1->relids, ec->ec_relids));
+
 		/*
 		 * Per the comment in have_relevant_eclass_joinclause, it's sufficient
 		 * to find an EC that mentions both this rel and some other rel.
 		 */
-		if (bms_overlap(rel1->relids, ec->ec_relids) &&
-			!bms_is_subset(ec->ec_relids, rel1->relids))
+		if (!bms_is_subset(ec->ec_relids, rel1->relids))
 			return true;
 	}
 
@@ -2449,11 +2540,9 @@ eclass_useful_for_merging(PlannerInfo *root,
 	Relids		relids;
 	ListCell   *lc;
 
-	Assert(!eclass->ec_merged);
-
 	/*
-	 * Won't generate joinclauses if const or single-member (the latter test
-	 * covers the volatile case too)
+	 * Won't generate joinclauses if const, zero or single-member (the latter
+	 * test covers the volatile case too)
 	 */
 	if (eclass->ec_has_const || list_length(eclass->ec_members) <= 1)
 		return false;
@@ -2556,3 +2645,51 @@ is_redundant_with_indexclauses(RestrictInfo *rinfo, List *indexclauses)
 
 	return false;
 }
+
+/*
+ * get_eclass_indexes_for_relids
+ *		Build and return a Bitmapset containing the indexes into root's
+ *		eq_classes list for all eclasses that have members for these relids
+ */
+static Bitmapset *
+get_eclass_indexes_for_relids(PlannerInfo *root, Relids relids)
+{
+	Bitmapset  *ec_indexes = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) > 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		ec_indexes = bms_add_members(ec_indexes, rel->eclass_indexes);
+	}
+	return ec_indexes;
+}
+
+/*
+ * get_common_eclass_indexes
+ *		Build and return a Bitmapset containing the indexes into root's
+ *		eq_classes list for all eclasses that relids1 and relids2 have in
+ *		common.
+ */
+static Bitmapset *
+get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
+{
+	Bitmapset  *rel1ecs;
+	Bitmapset  *rel2ecs;
+	int			relid;
+
+	rel1ecs = get_eclass_indexes_for_relids(root, relids1);
+
+	/*
+	 * We can get away with just using the eclass_indexes directly if relids2
+	 * is a singleton set.
+	 */
+	if (bms_get_singleton_member(relids2, &relid))
+		rel2ecs = root->simple_rel_array[relid]->eclass_indexes;
+	else
+		rel2ecs = get_eclass_indexes_for_relids(root, relids2);
+
+	/* Calculate and return the common EC indexes recycling the left input. */
+	return bms_int_members(rel1ecs, rel2ecs);
+}
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 253e0b7e48..9542633d19 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -692,6 +692,8 @@ typedef struct RelOptInfo
 											 * baserestrictinfo */
 	List	   *joininfo;		/* RestrictInfo structures for join clauses
 								 * involving this rel */
+	Bitmapset  *eclass_indexes;	/* Indexes into PlannerInfo's eq_classes that
+								 * know about this relation. */
 	bool		has_eclass_joins;	/* T means joininfo is incomplete */
 
 	/* used by partitionwise joins: */
@@ -917,7 +919,8 @@ typedef struct EquivalenceClass
 
 	List	   *ec_opfamilies;	/* btree operator family OIDs */
 	Oid			ec_collation;	/* collation, if datatypes are collatable */
-	List	   *ec_members;		/* list of EquivalenceMembers */
+	List	   *ec_members;		/* list of EquivalenceMembers, or NIL if EC
+								 * has been deleted and should be ignored */
 	List	   *ec_sources;		/* list of generating RestrictInfos */
 	List	   *ec_derives;		/* list of derived RestrictInfos */
 	Relids		ec_relids;		/* all relids appearing in ec_members, except
