diff-v30-v31.txt

text/plain

Filename: diff-v30-v31.txt
Type: text/plain
Part: 0
Message: Re: [PoC] Reducing planning time when tables have many partitions
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 7444622a527..d6a0dd524c4 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7829,35 +7829,12 @@ EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
 	PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
-	Relids		top_parent_rel_relids;
 	EquivalenceChildMemberIterator it;
 	EquivalenceMember *em;
 
-	/*
-	 * First, we translate the given Relids to their top-level parents. This
-	 * is required because an EquivalenceClass contains only parent
-	 * EquivalenceMembers, and we have to translate top-level ones to get
-	 * child members. We can skip such translations if we now see top-level
-	 * ones, i.e., when top_parent_rel is NULL. See the
-	 * find_relids_top_parents()'s definition for more details.
-	 */
-	top_parent_rel_relids = find_relids_top_parents(root, rel->relids);
-
-	/*
-	 * If we need to see child EquivalenceMembers, we access them via
-	 * EquivalenceChildMemberIterator during the iteration.
-	 */
-	setup_eclass_child_member_iterator(&it, ec);
+	setup_eclass_child_member_iterator(&it, root, ec, rel->relids);
 	while ((em = eclass_child_member_iterator_next(&it)))
 	{
-		/*
-		 * If child EquivalenceMembers may match the request, we add and
-		 * iterate over them by calling iterate_child_rel_equivalences().
-		 */
-		if (top_parent_rel_relids != NULL && !em->em_is_child &&
-			bms_is_subset(em->em_relids, top_parent_rel_relids))
-			iterate_child_rel_equivalences(&it, root, ec, em, rel->relids);
-
 		/*
 		 * Note we require !bms_is_empty, else we'd accept constant
 		 * expressions which are not suitable for the purpose.
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 0395cee9a7c..173449080f0 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -79,7 +79,6 @@ static JoinDomain *find_join_domain(PlannerInfo *root, Relids relids);
 static void add_transformed_child_version(EquivalenceChildMemberIterator *it,
 										  PlannerInfo *root,
 										  EquivalenceClass *ec,
-										  EquivalenceMember *parent_em,
 										  RelOptInfo *child_rel);
 static Bitmapset *get_eclass_indexes_for_relids(PlannerInfo *root,
 												Relids relids);
@@ -551,8 +550,7 @@ make_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	em->em_datatype = datatype;
 	em->em_jdomain = jdomain;
 	em->em_parent = parent;
-	em->em_child_relids = NULL;
-	em->em_child_joinrel_relids = NULL;
+	em->em_ec = ec;
 
 	if (bms_is_empty(relids))
 	{
@@ -581,9 +579,10 @@ make_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
  * add_parent_eq_member - build a new parent EquivalenceMember and add it to an EC
  *
  * Note: We don't have a function to add a child member like
- * add_child_eq_member() because how to do it depends on the relations they are
- * translated from. See add_child_rel_equivalences() and
- * add_child_join_rel_equivalences() to see how to add child members.
+ * add_child_eq_member() because how to do it depends on the relations they
+ * are translated from. See add_child_rel_equivalences(),
+ * add_child_join_rel_equivalences() and add_setop_child_rel_equivalences()
+ * to see how to add child members.
  */
 static EquivalenceMember *
 add_parent_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
@@ -805,17 +804,6 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	EquivalenceMember *newem;
 	ListCell   *lc1;
 	MemoryContext oldcontext;
-	Relids		top_parent_rel;
-
-	/*
-	 * First, we translate the given Relids to their top-level parents. This
-	 * is required because an EquivalenceClass contains only parent
-	 * EquivalenceMembers, and we have to translate top-level ones to get
-	 * child members. We can skip such translations if we now see top-level
-	 * ones, i.e., when top_parent_rel is NULL. See the
-	 * find_relids_top_parents()'s definition for more details.
-	 */
-	top_parent_rel = find_relids_top_parents(root, rel);
 
 	/*
 	 * Ensure the expression exposes the correct type and collation.
@@ -850,28 +838,15 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		/*
-		 * If we need to see child EquivalenceMembers, we access them via
-		 * EquivalenceChildMemberIterator during the iteration.
-		 */
-		setup_eclass_child_member_iterator(&it, cur_ec);
+		setup_eclass_child_member_iterator(&it, root, cur_ec, rel);
 		while ((cur_em = eclass_child_member_iterator_next(&it)) != NULL)
 		{
 			/*
-			 * If child EquivalenceMembers may match the request, we add and
-			 * iterate over them by calling iterate_child_rel_equivalences().
-			 */
-			if (top_parent_rel != NULL && !cur_em->em_is_child &&
-				bms_equal(cur_em->em_relids, top_parent_rel))
-				iterate_child_rel_equivalences(&it, root, cur_ec, cur_em, rel);
-
-			/*
-			 * Ignore child members unless they match the request. If this
-			 * EquivalenceMember is a child, i.e., translated above, it should
-			 * match the request. We cannot assert this if a request is
-			 * bms_is_subset().
+			 * Ignore child members unless they match the request.
 			 */
-			Assert(!cur_em->em_is_child || bms_equal(cur_em->em_relids, rel));
+			if (cur_em->em_is_child &&
+				!bms_equal(cur_em->em_relids, rel))
+				continue;
 
 			/*
 			 * Match constants only within the same JoinDomain (see
@@ -996,29 +971,14 @@ find_ec_member_matching_expr(PlannerInfo *root, EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
-	Relids		top_parent_relids;
 	EquivalenceChildMemberIterator it;
 	EquivalenceMember *em;
 
-	/*
-	 * First, we translate the given Relids to their top-level parents. This
-	 * is required because an EquivalenceClass contains only parent
-	 * EquivalenceMembers, and we have to translate top-level ones to get
-	 * child members. We can skip such translations if we now see top-level
-	 * ones, i.e., when top_parent_rel is NULL. See the
-	 * find_relids_top_parents()'s definition for more details.
-	 */
-	top_parent_relids = find_relids_top_parents(root, relids);
-
 	/* We ignore binary-compatible relabeling on both ends */
 	while (expr && IsA(expr, RelabelType))
 		expr = ((RelabelType *) expr)->arg;
 
-	/*
-	 * If we need to see child EquivalenceMembers, we access them via
-	 * EquivalenceChildMemberIterator during the iteration.
-	 */
-	setup_eclass_child_member_iterator(&it, ec);
+	setup_eclass_child_member_iterator(&it, root, ec, relids);
 	while ((em = eclass_child_member_iterator_next(&it)) != NULL)
 	{
 		Expr	   *emexpr;
@@ -1030,14 +990,6 @@ find_ec_member_matching_expr(PlannerInfo *root, EquivalenceClass *ec,
 		if (em->em_is_const)
 			continue;
 
-		/*
-		 * If child EquivalenceMembers may match the request, we add and
-		 * iterate over them by calling iterate_child_rel_equivalences().
-		 */
-		if (top_parent_relids != NULL && !em->em_is_child &&
-			bms_is_subset(em->em_relids, top_parent_relids))
-			iterate_child_rel_equivalences(&it, root, ec, em, relids);
-
 		/*
 		 * Ignore child members unless they belong to the requested rel.
 		 */
@@ -1098,7 +1050,6 @@ find_computable_ec_member(PlannerInfo *root,
 						  bool require_parallel_safe)
 {
 	List	   *exprvars;
-	Relids		top_parent_relids;
 	EquivalenceChildMemberIterator it;
 	EquivalenceMember *em;
 
@@ -1113,21 +1064,7 @@ find_computable_ec_member(PlannerInfo *root,
 							   PVC_INCLUDE_WINDOWFUNCS |
 							   PVC_INCLUDE_PLACEHOLDERS);
 
-	/*
-	 * First, we translate the given Relids to their top-level parents. This
-	 * is required because an EquivalenceClass contains only parent
-	 * EquivalenceMembers, and we have to translate top-level ones to get
-	 * child members. We can skip such translations if we now see top-level
-	 * ones, i.e., when top_parent_rel is NULL. See the
-	 * find_relids_top_parents()'s definition for more details.
-	 */
-	top_parent_relids = find_relids_top_parents(root, relids);
-
-	/*
-	 * If we need to see child EquivalenceMembers, we access them via
-	 * EquivalenceChildMemberIterator during the iteration.
-	 */
-	setup_eclass_child_member_iterator(&it, ec);
+	setup_eclass_child_member_iterator(&it, root, ec, relids);
 	while ((em = eclass_child_member_iterator_next(&it)) != NULL)
 	{
 		List	   *emvars;
@@ -1140,14 +1077,6 @@ find_computable_ec_member(PlannerInfo *root,
 		if (em->em_is_const)
 			continue;
 
-		/*
-		 * If child EquivalenceMembers may match the request, we add and
-		 * iterate over them by calling iterate_child_rel_equivalences().
-		 */
-		if (top_parent_relids != NULL && !em->em_is_child &&
-			bms_is_subset(em->em_relids, top_parent_relids))
-			iterate_child_rel_equivalences(&it, root, ec, em, relids);
-
 		/*
 		 * Ignore child members unless they belong to the requested rel.
 		 */
@@ -1848,20 +1777,9 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 	List	   *new_members = NIL;
 	List	   *outer_members = NIL;
 	List	   *inner_members = NIL;
-	Relids		top_parent_join_relids;
 	EquivalenceChildMemberIterator it;
 	EquivalenceMember *cur_em;
 
-	/*
-	 * First, we translate the given Relids to their top-level parents. This
-	 * is required because an EquivalenceClass contains only parent
-	 * EquivalenceMembers, and we have to translate top-level ones to get
-	 * child members. We can skip such translations if we now see top-level
-	 * ones, i.e., when top_parent_rel is NULL. See the
-	 * find_relids_top_parents()'s definition for more details.
-	 */
-	top_parent_join_relids = find_relids_top_parents(root, join_relids);
-
 	/*
 	 * First, scan the EC to identify member values that are computable at the
 	 * outer rel, at the inner rel, or at this relation but not in either
@@ -1870,21 +1788,10 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 	 * enforce that any newly computable members are all equal to each other
 	 * as well as to at least one input member, plus enforce at least one
 	 * outer-rel member equal to at least one inner-rel member.
-	 *
-	 * If we need to see child EquivalenceMembers, we access them via
-	 * EquivalenceChildMemberIterator during the iteration.
 	 */
-	setup_eclass_child_member_iterator(&it, ec);
+	setup_eclass_child_member_iterator(&it, root, ec, join_relids);
 	while ((cur_em = eclass_child_member_iterator_next(&it)) != NULL)
 	{
-		/*
-		 * If child EquivalenceMembers may match the request, we add and
-		 * iterate over them by calling iterate_child_rel_equivalences().
-		 */
-		if (top_parent_join_relids != NULL && !cur_em->em_is_child &&
-			bms_is_subset(cur_em->em_relids, top_parent_join_relids))
-			iterate_child_rel_equivalences(&it, root, ec, cur_em, join_relids);
-
 		/*
 		 * We don't need to check explicitly for child EC members.  This test
 		 * against join_relids will cause them to be ignored except when
@@ -3058,8 +2965,8 @@ add_child_rel_equivalences(PlannerInfo *root,
 			 * combinations of children.  (But add_child_join_rel_equivalences
 			 * may add targeted combinations for partitionwise-join purposes.)
 			 */
-			/* Child members should not exist in ec_members */
-			Assert(!cur_em->em_is_child);
+			Assert(!cur_em->em_is_child);	/* Child members should not exist
+											 * in ec_members */
 
 			/*
 			 * Consider only members that reference and can be computed at
@@ -3111,15 +3018,6 @@ add_child_rel_equivalences(PlannerInfo *root,
 				child_rel->eclass_child_members = lappend(child_rel->eclass_child_members,
 														  child_em);
 
-				/*
-				 * We save the knowledge that 'child_em' can be translated
-				 * using 'child_rel'. This knowledge is useful for
-				 * iterate_child_rel_equivalences() to find child members from
-				 * the given Relids.
-				 */
-				cur_em->em_child_relids = bms_add_member(cur_em->em_child_relids,
-														 child_rel->relid);
-
 				/* Record this EC index for the child rel */
 				child_rel->eclass_indexes = bms_add_member(child_rel->eclass_indexes, i);
 			}
@@ -3192,8 +3090,8 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 			 * We consider only original EC members here, not
 			 * already-transformed child members.
 			 */
-			/* Child members should not exist in ec_members */
-			Assert(!cur_em->em_is_child);
+			Assert(!cur_em->em_is_child);	/* Child members should not exist
+											 * in ec_members */
 
 			/*
 			 * We may ignore expressions that reference a single baserel,
@@ -3246,16 +3144,6 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 				child_joinrel->eclass_child_members =
 					lappend(child_joinrel->eclass_child_members, child_em);
 
-				/*
-				 * We save the knowledge that 'child_em' can be translated
-				 * using 'child_joinrel'. This knowledge is useful for
-				 * iterate_child_rel_equivalences() to find child members from
-				 * the given Relids.
-				 */
-				cur_em->em_child_joinrel_relids =
-					bms_add_member(cur_em->em_child_joinrel_relids,
-								   child_joinrel->join_rel_list_index);
-
 				/*
 				 * Update the corresponding inverted indexes.
 				 */
@@ -3271,7 +3159,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 					 * that have only such parent relations as em_relids are
 					 * already present in the ec_members, and so cannot be
 					 * candidates for additional iteration by
-					 * iterate_child_rel_equivalences(). Since the function
+					 * EquivalenceChildMemberIterator. Since the iterator
 					 * needs EquivalenceMembers whose em_relids has child
 					 * relations, skipping the update of this inverted index
 					 * allows for faster iteration.
@@ -3341,15 +3229,6 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 		child_rel->eclass_child_members =
 			lappend(child_rel->eclass_child_members, child_em);
 
-		/*
-		 * We save the knowledge that 'child_em' can be translated using
-		 * 'child_rel'. This knowledge is useful for
-		 * iterate_child_rel_equivalences() to find child members from the
-		 * given Relids.
-		 */
-		parent_em->em_child_relids =
-			bms_add_member(parent_em->em_child_relids, child_rel->relid);
-
 		/*
 		 * Make an UNION parent-child relationship between parent_em and
 		 * child_rel->relid. We record this relationship in
@@ -3399,40 +3278,125 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
  */
 void
 setup_eclass_child_member_iterator(EquivalenceChildMemberIterator *it,
-								   EquivalenceClass *ec)
+								   PlannerInfo *root,
+								   EquivalenceClass *ec,
+								   Relids relids)
 {
+	Bitmapset  *matching_indexes;
+	int			i;
+
+	/*
+	 * Initialize the iterator.
+	 */
 	it->index = -1;
 	it->modified = false;
 	it->ec_members = ec->ec_members;
-}
 
-/*
- * eclass_child_member_iterator_next
- *	  Get a next EquivalenceMember from an EquivalenceChildMemberIterator 'it'
- *	  that was setup by setup_eclass_child_member_iterator(). NULL is returned
- * 	  if there are no members left.
- */
-EquivalenceMember *
-eclass_child_member_iterator_next(EquivalenceChildMemberIterator *it)
-{
-	if (++it->index < list_length(it->ec_members))
-		return list_nth_node(EquivalenceMember, it->ec_members, it->index);
-	return NULL;
-}
+	/*
+	 * If there are no child relations, there is nothing to do. This
+	 * effectively avoids regression for non-partitioned cases.
+	 */
+	if (root->top_parent_relid_array == NULL)
+		return;
 
-/*
- * dispose_eclass_child_member_iterator
- *	  Free any memory allocated by the iterator.
- */
-void
-dispose_eclass_child_member_iterator(EquivalenceChildMemberIterator *it)
-{
 	/*
-	 * XXX Should we use list_free()? I decided to use this style to take
-	 * advantage of speculative execution.
+	 * EquivalenceClass->ec_members has only parent EquivalenceMembers. Child
+	 * members are translated using child RelOptInfos and stored in them. This
+	 * is done in add_child_rel_equivalences(),
+	 * add_child_join_rel_equivalences(), and
+	 * add_setop_child_rel_equivalences(). To retrieve child
+	 * EquivalenceMembers of some parent, we need to know which RelOptInfos
+	 * have such child members. We can know this information using indexes
+	 * like EquivalenceClassIndexes->joinrel_indexes.
+	 *
+	 * We use an inverted index mechanism to quickly iterate over the members
+	 * whose em_relids is a subset of the given 'child_relids'. The inverted
+	 * indexes store RelOptInfo indices that have EquivalenceMembers
+	 * mentioning them. Taking the union of these indexes allows to find which
+	 * RelOptInfos have the EquivalenceMember we are looking for. With this
+	 * method, the em_relids of the newly iterated ones overlap the given
+	 * 'child_relids', but may not be subsets, so the caller must check that
+	 * they satisfy the desired condition.
+	 *
+	 * The above comments are about joinrels, and for simple rels, this
+	 * mechanism is simpler. It is sufficient to simply add the child
+	 * EquivalenceMembers of RelOptInfo to the iterator.
+	 *
+	 * We need to perform these steps for each of the two types of relations.
 	 */
-	if (unlikely(it->modified))
-		pfree(it->ec_members);
+
+	/*
+	 * Iterate over the given relids, adding child members for simple rels and
+	 * taking union indexes for join rels.
+	 */
+	i = -1;
+	matching_indexes = NULL;
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *child_rel;
+		EquivalenceClassIndexes *indexes;
+
+		/*
+		 * If this relation is a parent, we don't have to do anything.
+		 */
+		if (root->top_parent_relid_array[i] == -1)
+			continue;
+
+		/*
+		 * Add child members that mention this relation to the iterator.
+		 */
+		child_rel = root->simple_rel_array[i];
+		if (child_rel != NULL)
+			add_transformed_child_version(it, root, ec, child_rel);
+
+		/*
+		 * Union indexes for join rels.
+		 */
+		indexes = &root->eclass_indexes_array[i];
+		matching_indexes =
+			bms_add_members(matching_indexes, indexes->joinrel_indexes);
+	}
+
+	/*
+	 * For join rels, add child members using 'matching_indexes'.
+	 */
+	i = -1;
+	while ((i = bms_next_member(matching_indexes, i)) >= 0)
+	{
+		RelOptInfo *child_joinrel =
+			list_nth_node(RelOptInfo, root->join_rel_list, i);
+
+		Assert(child_joinrel != NULL);
+
+		/*
+		 * If this joinrel's Relids is not a subset of the given one, then the
+		 * child EquivalenceMembers it holds should never be a subset either.
+		 */
+		if (bms_is_subset(child_joinrel->relids, relids))
+			add_transformed_child_version(it, root, ec, child_joinrel);
+#ifdef USE_ASSERT_CHECKING
+		else
+		{
+			/*
+			 * Verify that the above comment is correct.
+			 *
+			 * NOTE: We may remove this assertion after the beta process.
+			 */
+
+			ListCell   *lc;
+
+			foreach(lc, child_joinrel->eclass_child_members)
+			{
+				EquivalenceMember *child_em = lfirst_node(EquivalenceMember, lc);
+
+				if (child_em->em_ec != ec)
+					continue;
+				Assert(!bms_is_subset(child_em->em_relids, relids));
+			}
+		}
+#endif
+	}
+	bms_free(matching_indexes);
 }
 
 /*
@@ -3441,13 +3405,12 @@ dispose_eclass_child_member_iterator(EquivalenceChildMemberIterator *it)
  *	  the iterator.
  *
  * This function is expected to be called only from
- * iterate_child_rel_equivalences().
+ * setup_eclass_child_member_iterator().
  */
 static void
 add_transformed_child_version(EquivalenceChildMemberIterator *it,
 							  PlannerInfo *root,
 							  EquivalenceClass *ec,
-							  EquivalenceMember *parent_em,
 							  RelOptInfo *child_rel)
 {
 	ListCell   *lc;
@@ -3457,7 +3420,7 @@ add_transformed_child_version(EquivalenceChildMemberIterator *it,
 		EquivalenceMember *child_em = lfirst_node(EquivalenceMember, lc);
 
 		/* Skip unwanted EquivalenceMembers */
-		if (child_em->em_parent != parent_em)
+		if (child_em->em_ec != ec)
 			continue;
 
 		/*
@@ -3476,136 +3439,32 @@ add_transformed_child_version(EquivalenceChildMemberIterator *it,
 }
 
 /*
- * iterate_child_rel_equivalences
- *	  Add child EquivalenceMembers whose em_relids is a subset of the given
- *	  'child_relids' to the iterator. Note that this function may add false
- *	  positives, so callers must check that the members that this function adds
- *	  satisfy the desired condition.
- *
- * The transformation is done in add_transformed_child_version().
+ * eclass_child_member_iterator_next
+ *	  Get a next EquivalenceMember from an EquivalenceChildMemberIterator 'it'
+ *	  that was setup by setup_eclass_child_member_iterator(). NULL is returned
+ * 	  if there are no members left.
  */
-void
-iterate_child_rel_equivalences(EquivalenceChildMemberIterator *it,
-							   PlannerInfo *root,
-							   EquivalenceClass *ec,
-							   EquivalenceMember *parent_em,
-							   Relids child_relids)
+EquivalenceMember *
+eclass_child_member_iterator_next(EquivalenceChildMemberIterator *it)
 {
-	/* The given EquivalenceMember should be parent */
-	Assert(!parent_em->em_is_child);
-
-	/*
-	 * EquivalenceClass->ec_members has only parent EquivalenceMembers. Child
-	 * members are translated using child RelOptInfos and stored in them. This
-	 * is done in add_child_rel_equivalences() and
-	 * add_child_join_rel_equivalences(). To retrieve child EquivalenceMembers
-	 * of some parent, we need to know which RelOptInfos have such child
-	 * members. We can know this information using indexes named
-	 * EquivalenceMember->em_child_relids,
-	 * EquivalenceMember->em_child_joinrel_relids, and
-	 * EquivalenceClassIndexes->joinrel_indexes.
-	 *
-	 * We use an inverted index mechanism to quickly iterate over the members
-	 * whose em_relids is a subset of the given 'child_relids'. The inverted
-	 * indexes store RelOptInfo indices that have EquivalenceMembers
-	 * mentioning them. Taking the union of these indexes allows to find which
-	 * RelOptInfos have the EquivalenceMember we are looking for. With this
-	 * method, the em_relids of the newly iterated ones overlap the given
-	 * 'child_relids', but may not be subsets, so the caller must check that
-	 * they satisfy the desired condition.
-	 *
-	 * The above comments are about joinrels, and for simple rels, this
-	 * mechanism is simpler. It is sufficient to simply add the child
-	 * EquivalenceMembers of RelOptInfo to the iterator.
-	 *
-	 * We need to perform these steps for each of the two types of relations.
-	 */
-
-	/*
-	 * First, we translate simple rels.
-	 */
-	if (!bms_is_empty(parent_em->em_child_relids))
-	{
-		Relids		matching_relids;
-		int			i;
-
-		/* Step 1: Get simple rels' Relids that have wanted EMs. */
-		matching_relids = bms_intersect(parent_em->em_child_relids,
-										child_relids);
-
-		/* Step 2: Fetch wanted EMs. */
-		i = -1;
-		while ((i = bms_next_member(matching_relids, i)) >= 0)
-		{
-			RelOptInfo *child_rel = root->simple_rel_array[i];
-
-			Assert(child_rel != NULL);
-			add_transformed_child_version(it, root, ec, parent_em, child_rel);
-		}
-		bms_free(matching_relids);
-	}
+	if (++it->index < list_length(it->ec_members))
+		return list_nth_node(EquivalenceMember, it->ec_members, it->index);
+	return NULL;
+}
 
+/*
+ * dispose_eclass_child_member_iterator
+ *	  Free any memory allocated by the iterator.
+ */
+void
+dispose_eclass_child_member_iterator(EquivalenceChildMemberIterator *it)
+{
 	/*
-	 * Next, we try to translate join rels.
+	 * XXX Should we use list_free()? I decided to use this style to take
+	 * advantage of speculative execution.
 	 */
-	if (!bms_is_empty(parent_em->em_child_joinrel_relids))
-	{
-		Bitmapset  *matching_indexes;
-		int			i;
-
-		/* Step 1: Get join rels' indices that have wanted EMs. */
-		i = -1;
-		matching_indexes = NULL;
-		while ((i = bms_next_member(child_relids, i)) >= 0)
-		{
-			EquivalenceClassIndexes *indexes = &root->eclass_indexes_array[i];
-
-			matching_indexes =
-				bms_add_members(matching_indexes, indexes->joinrel_indexes);
-		}
-		matching_indexes = bms_int_members(matching_indexes,
-										   parent_em->em_child_joinrel_relids);
-
-		/* Step 2: Fetch wanted EMs. */
-		i = -1;
-		while ((i = bms_next_member(matching_indexes, i)) >= 0)
-		{
-			RelOptInfo *child_joinrel =
-				list_nth_node(RelOptInfo, root->join_rel_list, i);
-
-			Assert(child_joinrel != NULL);
-
-			/*
-			 * If this joinrel's Relids is not a subset of the given one, then
-			 * the child EquivalenceMembers it holds should never be a subset
-			 * either.
-			 */
-			if (bms_is_subset(child_joinrel->relids, child_relids))
-				add_transformed_child_version(it, root, ec, parent_em, child_joinrel);
-#ifdef USE_ASSERT_CHECKING
-			else
-			{
-				/*
-				 * Verify that the above comment is correct.
-				 *
-				 * NOTE: We may remove this assertion after the beta process.
-				 */
-
-				ListCell   *lc;
-
-				foreach(lc, child_joinrel->eclass_child_members)
-				{
-					EquivalenceMember *child_em = lfirst_node(EquivalenceMember, lc);
-
-					if (child_em->em_parent != parent_em)
-						continue;
-					Assert(!bms_is_subset(child_em->em_relids, child_relids));
-				}
-			}
-#endif
-		}
-		bms_free(matching_indexes);
-	}
+	if (unlikely(it->modified))
+		pfree(it->ec_members);
 }
 
 
@@ -3641,8 +3500,7 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 {
 	List	   *result = NIL;
 	bool		is_child_rel = (rel->reloptkind == RELOPT_OTHER_MEMBER_REL);
-	Relids		parent_relids,
-				top_parent_rel_relids;
+	Relids		parent_relids;
 	int			i;
 
 	/* Should be OK to rely on eclass_indexes */
@@ -3651,16 +3509,6 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 	/* Indexes are available only on base or "other" member relations. */
 	Assert(IS_SIMPLE_REL(rel));
 
-	/*
-	 * First, we translate the given Relids to their top-level parents. This
-	 * is required because an EquivalenceClass contains only parent
-	 * EquivalenceMembers, and we have to translate top-level ones to get
-	 * child members. We can skip such translations if we now see top-level
-	 * ones, i.e., when top_parent_rel is NULL. See the
-	 * find_relids_top_parents()'s definition for more details.
-	 */
-	top_parent_rel_relids = find_relids_top_parents(root, rel->relids);
-
 	/* If it's a child rel, we'll need to know what its parent(s) are */
 	if (is_child_rel)
 		parent_relids = find_childrel_parents(root, rel);
@@ -3694,20 +3542,10 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 		 * column gets matched to.  This is annoying but it only happens in
 		 * corner cases, so for now we live with just reporting the first
 		 * match.  See also get_eclass_for_sort_expr.)
-		 *
-		 * If we need to see child EquivalenceMembers, we access them via
-		 * EquivalenceChildMemberIterator during the iteration.
 		 */
-		setup_eclass_child_member_iterator(&it, cur_ec);
+		setup_eclass_child_member_iterator(&it, root, cur_ec, rel->relids);
 		while ((cur_em = eclass_child_member_iterator_next(&it)) != NULL)
 		{
-			/*
-			 * If child EquivalenceMembers may match the request, we add and
-			 * iterate over them by calling iterate_child_rel_equivalences().
-			 */
-			if (top_parent_rel_relids != NULL && !cur_em->em_is_child &&
-				bms_equal(cur_em->em_relids, top_parent_rel_relids))
-				iterate_child_rel_equivalences(&it, root, cur_ec, cur_em, rel->relids);
 			if (bms_equal(cur_em->em_relids, rel->relids) &&
 				callback(root, rel, cur_ec, cur_em, callback_arg))
 				break;
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 7ced5e33798..d19b45a1293 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -3743,19 +3743,8 @@ match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index, List *pathkeys,
 						List **orderby_clauses_p,
 						List **clause_columns_p)
 {
-	Relids		top_parent_index_relids;
 	ListCell   *lc1;
 
-	/*
-	 * First, we translate the given Relids to their top-level parents. This
-	 * is required because an EquivalenceClass contains only parent
-	 * EquivalenceMembers, and we have to translate top-level ones to get
-	 * child members. We can skip such translations if we now see top-level
-	 * ones, i.e., when top_parent_rel is NULL. See the
-	 * find_relids_top_parents()'s definition for more details.
-	 */
-	top_parent_index_relids = find_relids_top_parents(root, index->rel->relids);
-
 	*orderby_clauses_p = NIL;	/* set default results */
 	*clause_columns_p = NIL;
 
@@ -3787,36 +3776,17 @@ match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index, List *pathkeys,
 		 * child member of more than one EC.  Therefore, the same index could
 		 * be considered to match more than one pathkey list, which is OK
 		 * here.  See also get_eclass_for_sort_expr.)
-		 *
-		 * If we need to see child EquivalenceMembers, we access them via
-		 * EquivalenceChildMemberIterator during the iteration.
 		 */
-		setup_eclass_child_member_iterator(&it, pathkey->pk_eclass);
+		setup_eclass_child_member_iterator(&it, root, pathkey->pk_eclass,
+										   index->rel->relids);
 		while ((member = eclass_child_member_iterator_next(&it)))
 		{
 			int			indexcol;
 
-			/*
-			 * If child EquivalenceMembers may match the request, we add and
-			 * iterate over them by calling iterate_child_rel_equivalences().
-			 */
-			if (top_parent_index_relids != NULL && !member->em_is_child &&
-				bms_equal(member->em_relids, top_parent_index_relids))
-				iterate_child_rel_equivalences(&it, root, pathkey->pk_eclass, member,
-											   index->rel->relids);
-
 			/* No possibility of match if it references other relations */
-			if (!member->em_is_child &&
-				!bms_equal(member->em_relids, index->rel->relids))
+			if (!bms_equal(member->em_relids, index->rel->relids))
 				continue;
 
-			/*
-			 * If this EquivalenceMember is a child, i.e., translated above,
-			 * it should match the request. We cannot assert this if a request
-			 * is bms_is_subset().
-			 */
-			Assert(bms_equal(member->em_relids, index->rel->relids));
-
 			/*
 			 * We allow any column of the index to match each pathkey; they
 			 * don't have to match left-to-right as you might expect.  This is
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 48517ddaf86..c8e535617b8 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -1587,48 +1587,6 @@ find_childrel_parents(PlannerInfo *root, RelOptInfo *rel)
 }
 
 
-/*
- * find_relids_top_parents_slow
- *	  Slow path of find_relids_top_parents() macro.
- *
- * Do not call this directly; use the macro instead. See the macro comment for
- * more details.
- */
-Relids
-find_relids_top_parents_slow(PlannerInfo *root, Relids relids)
-{
-	Index	   *top_parent_relid_array = root->top_parent_relid_array;
-	Relids		result;
-	bool		is_top_parent;
-	int			i;
-
-	/* This function should be called in the slow path */
-	Assert(top_parent_relid_array != NULL);
-
-	result = NULL;
-	is_top_parent = true;
-	i = -1;
-	while ((i = bms_next_member(relids, i)) >= 0)
-	{
-		int			top_parent_relid = (int) top_parent_relid_array[i];
-
-		if (top_parent_relid == -1)
-			top_parent_relid = i;	/* 'i' has no parents, so add itself */
-		else if (top_parent_relid != i)
-			is_top_parent = false;
-		result = bms_add_member(result, top_parent_relid);
-	}
-
-	if (is_top_parent)
-	{
-		bms_free(result);
-		return NULL;
-	}
-
-	return result;
-}
-
-
 /*
  * get_baserel_parampathinfo
  *		Get the ParamPathInfo for a parameterized path for a base relation,
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index a408051faba..8b4c8319aad 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -1527,35 +1527,32 @@ typedef struct EquivalenceMember
 	bool		em_is_child;	/* derived version for a child relation? */
 	Oid			em_datatype;	/* the "nominal type" used by the opfamily */
 	JoinDomain *em_jdomain;		/* join domain containing the source clause */
-	Relids		em_child_relids;	/* all relids of child rels */
-	Bitmapset  *em_child_joinrel_relids;	/* indexes in root->join_rel_list
-											 * of join rel children */
 	/* if em_is_child is true, this links to corresponding EM for top parent */
 	struct EquivalenceMember *em_parent pg_node_attr(read_write_ignore);
+	EquivalenceClass *em_ec;	/* EquivalenceClass which has this member */
 } EquivalenceMember;
 
 /*
  * EquivalenceChildMemberIterator
  *
- * EquivalenceClass has only parent EquivalenceMembers, so we need to translate
- * child members if necessary. EquivalenceChildMemberIterator provides a way to
- * iterate over translated child members during the loop in addition to all of
- * the parent EquivalenceMembers.
+ * EquivalenceClass contains only parent members. Use the
+ * EquivalenceChildMemberIterator to iterate over child members whose em_relids
+ * is a subset of the specified 'child_relids' in addition to the parent
+ * members. Note that the iterator may yield false positives, so callers must
+ * verify that each member satisfies the condition.
  *
  * The most common way to use this struct is as follows:
  * -----
+ * PlannerInfo					   *root = given;
  * EquivalenceClass				   *ec = given;
  * Relids							rel = given;
  * EquivalenceChildMemberIterator	it;
  * EquivalenceMember			   *em;
  *
- * setup_eclass_child_member_iterator(&it, ec);
+ * setup_eclass_child_member_iterator(&it, root, ec, rel);
  * while ((em = eclass_child_member_iterator_next(&it)) != NULL)
  * {
  *     use em ...;
- *     if (we need to iterate over child EquivalenceMembers)
- *         iterate_child_rel_equivalences(&it, root, ec, em, rel);
- *     use em ...;
  * }
  * dispose_eclass_child_member_iterator(&it);
  * -----
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index b6208928bf9..719be3897f6 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -331,24 +331,6 @@ extern Relids min_join_parameterization(PlannerInfo *root,
 extern RelOptInfo *fetch_upper_rel(PlannerInfo *root, UpperRelationKind kind,
 								   Relids relids);
 extern Relids find_childrel_parents(PlannerInfo *root, RelOptInfo *rel);
-
-/*
- * find_relids_top_parents
- *	  Compute the set of top-parent relids of rel.
- *
- * Replaces all Relids appearing in the given 'relids' as their top-level
- * parents. The result will be NULL if and only if all of the given relids are
- * top-level.
- *
- * The motivation for having this feature as a macro rather than a function is
- * that Relids are top-level in most cases. We can quickly determine when
- * root->top_parent_relid_array is NULL.
- */
-#define find_relids_top_parents(root, relids) \
-	((root)->top_parent_relid_array == NULL \
-	 ? NULL : find_relids_top_parents_slow(root, relids))
-extern Relids find_relids_top_parents_slow(PlannerInfo *root, Relids relids);
-
 extern ParamPathInfo *get_baserel_parampathinfo(PlannerInfo *root,
 												RelOptInfo *baserel,
 												Relids required_outer);
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index a91fb41335a..07525dca66d 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -184,14 +184,11 @@ extern void add_setop_child_rel_equivalences(PlannerInfo *root,
 											 List *child_tlist,
 											 List *setop_pathkeys);
 extern void setup_eclass_child_member_iterator(EquivalenceChildMemberIterator *it,
-											   EquivalenceClass *ec);
+											   PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
 extern EquivalenceMember *eclass_child_member_iterator_next(EquivalenceChildMemberIterator *it);
 extern void dispose_eclass_child_member_iterator(EquivalenceChildMemberIterator *it);
-extern void iterate_child_rel_equivalences(EquivalenceChildMemberIterator *it,
-										   PlannerInfo *root,
-										   EquivalenceClass *ec,
-										   EquivalenceMember *parent_em,
-										   Relids child_relids);
 extern List *generate_implied_equalities_for_column(PlannerInfo *root,
 													RelOptInfo *rel,
 													ec_matches_callback_type callback,