v6-0003-Implement-iterators.patch

application/octet-stream

Filename: v6-0003-Implement-iterators.patch
Type: application/octet-stream
Part: 8
Message: Re: [PoC] Reducing planning time when tables have many partitions

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: format-patch
Series: patch v6-0003
Subject: Implement iterators
File+
contrib/postgres_fdw/postgres_fdw.c 9 15
src/backend/optimizer/path/equivclass.c 312 70
src/include/nodes/pathnodes.h 68 0
src/include/optimizer/paths.h 148 2
From fcef5092a17e36fdd5798096ee362aff2b23f55c Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Wed, 14 Sep 2022 09:56:42 +0900
Subject: [PATCH v6 3/3] Implement iterators

---
 contrib/postgres_fdw/postgres_fdw.c     |  24 +-
 src/backend/optimizer/path/equivclass.c | 382 +++++++++++++++++++-----
 src/include/nodes/pathnodes.h           |  68 +++++
 src/include/optimizer/paths.h           | 150 +++++++++-
 4 files changed, 537 insertions(+), 87 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index e8996456be..e4f6aa33b1 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7412,15 +7412,13 @@ conversion_error_callback(void *arg)
 EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
-	Bitmapset *matching_ems;
-	int		i = -1;
+	EquivalenceMember *em;
+	EquivalenceMemberIterator it;
 
-	matching_ems = get_ecmember_indexes(root, ec, rel->relids, true, false);
+	get_ecmember_indexes_iterator(&it, root, ec, rel->relids, true, false, false);
 
-	while ((i = bms_next_member(matching_ems, i)) >= 0)
+	while ((em = get_next_matching_member(root, &it)) != NULL)
 	{
-		EquivalenceMember *em = list_nth_node(EquivalenceMember, root->eq_members, i);
-
 		/*
 		 * Note we require !bms_is_empty, else we'd accept constant
 		 * expressions which are not suitable for the purpose.
@@ -7459,9 +7457,9 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 	{
 		Expr	   *expr = (Expr *) lfirst(lc1);
 		Index		sgref = get_pathtarget_sortgroupref(target, i);
-		Bitmapset  *matching_ems;
+		EquivalenceMember *em;
+		EquivalenceMemberStrictIterator it;
 		Relids		expr_relids;
-		int			j;
 
 		/* Ignore non-sort expressions */
 		if (sgref == 0 ||
@@ -7477,14 +7475,11 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 			expr = ((RelabelType *) expr)->arg;
 
 		expr_relids = pull_varnos(root, (Node *) expr);
-		matching_ems = get_ecmember_indexes_strict(root, ec, expr_relids,
-												   false, false);
+		get_ecmember_indexes_strict_iterator(&it, root, ec, expr_relids,
+											 false, false, false);
 		/* Locate an EquivalenceClass member matching this expr, if any */
-		j = -1;
-		while ((j = bms_next_member(matching_ems, j)) >= 0)
+		while ((em = get_next_matching_member_strict(root, &it)) != NULL)
 		{
-			EquivalenceMember *em = list_nth_node(EquivalenceMember,
-												  root->eq_members, j);
 			Expr	   *em_expr;
 
 			/* don't expect constants */
@@ -7507,7 +7502,6 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 		}
 		i++;
 		bms_free(expr_relids);
-		bms_free(matching_ems);
 	}
 
 	return NULL;
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 5f0d2bafd4..3207c7e35a 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -663,7 +663,34 @@ add_eq_member(PlannerInfo *root, EquivalenceClass *ec, Expr *expr,
 	root->eq_members = lappend(root->eq_members, em);
 
 	/* record exprs with no relids */
-	if (bms_is_empty(expr_relids))
+	/*
+	 * TODO: I had to use relids instead of expr_relids to pass regression tests.
+	 * This is probably related to get_ecmember_indexes[_strict]_iterator
+	 * functions, which is quoted below.
+	 *
+	 * =====
+	 * void
+	 * get_ecmember_indexes_iterator(...)
+	 * {
+	 * 	...
+	 * 	else
+	 * 	{
+	 * 		Bitmapset *index;
+	 *
+	 * 		it->check_on_iterate = !caller_needs_recheck;
+	 *
+	 * 		index =
+	 * 			with_children ? ec->ec_member_indexes : ec->ec_nonchild_indexes;
+	 * 		if (!with_norel_members)
+	 * 			index = bms_difference(index, ec->ec_norel_indexes);
+	 * 			^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+	 *
+	 * 		it->index = index;
+	 * 	}
+	 * }
+	 * =====
+	 */
+	if (bms_is_empty(relids))
 		ec->ec_norel_indexes = bms_add_member(ec->ec_norel_indexes, em_index);
 
 	if (is_child)
@@ -748,8 +775,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		Bitmapset		 *matching_ems;
-		int			i;
+		EquivalenceMember *cur_em;
+		EquivalenceMemberStrictIterator it;
 
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
@@ -764,15 +791,11 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		matching_ems = get_ecmember_indexes_strict(root, cur_ec, expr_relids,
-												   true, true);
+		get_ecmember_indexes_strict_iterator(&it, root, cur_ec, expr_relids,
+											 true, true, true);
 
-		i = -1;
-		while ((i = bms_next_member(matching_ems, i)) >= 0)
+		while ((cur_em = get_next_matching_member_strict(root, &it)) != NULL)
 		{
-			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
-													  root->eq_members, i);
-
 			/*
 			 * Ignore child members unless they match the request.
 			 */
@@ -909,22 +932,19 @@ find_ec_member_matching_expr(PlannerInfo *root,
 							 Expr *expr,
 							 Relids relids)
 {
-	Bitmapset  *matching_ems;
 	Relids		expr_relids;
-	int			i;
+	EquivalenceMember *em;
+	EquivalenceMemberStrictIterator it;
 
 	/* We ignore binary-compatible relabeling on both ends */
 	while (expr && IsA(expr, RelabelType))
 		expr = ((RelabelType *) expr)->arg;
 
 	expr_relids = pull_varnos(root, (Node *) expr);
-	matching_ems = get_ecmember_indexes_strict(root, ec, expr_relids, true, true);
+	get_ecmember_indexes_strict_iterator(&it, root, ec, expr_relids, true, true, true);
 
-	i = -1;
-	while ((i = bms_next_member(matching_ems, i)) >= 0)
+	while ((em = get_next_matching_member_strict(root, &it)) != NULL)
 	{
-		EquivalenceMember *em = list_nth_node(EquivalenceMember,
-											  root->eq_members, i);
 		Expr	   *emexpr;
 
 		/*
@@ -1693,9 +1713,9 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 	List	   *new_members = NIL;
 	List	   *outer_members = NIL;
 	List	   *inner_members = NIL;
-	Bitmapset  *matching_ems;
+	EquivalenceMember *cur_em;
+	EquivalenceMemberIterator it;
 	ListCell   *lc1;
-	int			i;
 
 	/*
 	 * First, scan the EC to identify member values that are computable at the
@@ -1706,14 +1726,10 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 	 * 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.
 	 */
-	matching_ems = get_ecmember_indexes(root, ec, join_relids, true, false);
+	get_ecmember_indexes_iterator(&it, root, ec, join_relids, true, false, true);
 
-	i = -1;
-	while ((i = bms_next_member(matching_ems, i)) >= 0)
+	while ((cur_em = get_next_matching_member(root, &it)) != NULL)
 	{
-		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
-												  root->eq_members, i);
-
 		/*
 		 * We don't need to check explicitly for child EC members.  This test
 		 * against join_relids will cause them to be ignored except when
@@ -1872,16 +1888,13 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 										Relids nominal_inner_relids,
 										RelOptInfo *inner_rel)
 {
-	Bitmapset  *matching_es;
 	List	   *result = NIL;
-	int			i;
+	RestrictInfo *restrictinfo;
+	RestrictInfoIterator it;
 
-	matching_es = get_ec_source_indexes(root, ec, nominal_join_relids);
-	i = -1;
-	while ((i = bms_next_member(matching_es, i)) >= 0)
+	get_ec_source_indexes_iterator(&it, root, ec, nominal_join_relids, true);
+	while ((restrictinfo = get_next_matching_rinfo(root, &it)) != NULL)
 	{
-		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo,
-												   root->eq_sources, i);
 		Relids		clause_relids = restrictinfo->required_relids;
 
 		if (bms_is_subset(clause_relids, nominal_join_relids) &&
@@ -2258,7 +2271,8 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		Bitmapset		 *matching_ems;
+		EquivalenceMember *cur_em;
+		EquivalenceMemberStrictIterator it;
 		bool		match;
 		int			i;
 
@@ -2275,15 +2289,11 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 			continue;
 		/* Does it contain a match to outervar? */
 		match = false;
-		matching_ems = get_ecmember_indexes_strict(root, cur_ec,
-												   outer_relids,
-												   false, true);
-		i = -1;
-		while ((i = bms_next_member(matching_ems, i)) >= 0)
+		get_ecmember_indexes_strict_iterator(&it, root, cur_ec,
+											 outer_relids,
+											 false, true, true);
+		while ((cur_em = get_next_matching_member_strict(root, &it)) != NULL)
 		{
-			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
-													  root->eq_members, i);
-
 			Assert(!cur_em->em_is_child);	/* no children yet */
 			if (equal(outervar, cur_em->em_expr))
 			{
@@ -2383,7 +2393,7 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
 		EquivalenceMember *coal_em = NULL;
-		Bitmapset		  *matching_ems;
+		EquivalenceMemberStrictIterator it;
 		bool		match;
 		bool		matchleft;
 		bool		matchright;
@@ -2415,14 +2425,12 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 		 * the two column types). Is it OK to strip implicit coercions from
 		 * the COALESCE arguments?
 		 */
-		matching_ems = get_ecmember_indexes_strict(root, cur_ec,
-												   rinfo->clause_relids, true,
-												   false);
+		get_ecmember_indexes_strict_iterator(&it, root, cur_ec,
+											 rinfo->clause_relids, true,
+											 false, true);
 		match = false;
-		i = -1;
-		while ((i = bms_next_member(matching_ems, i)) >= 0)
+		while ((coal_em = get_next_matching_member_strict(root, &it)) != NULL)
 		{
-			coal_em = list_nth_node(EquivalenceMember, root->eq_members, i);
 			Assert(!coal_em->em_is_child);	/* no children yet */
 			if (IsA(coal_em->em_expr, CoalesceExpr))
 			{
@@ -2437,7 +2445,7 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 
 				if (equal(leftvar, cfirst) && equal(rightvar, csecond))
 				{
-					coal_idx = i;
+					coal_idx = it.position;
 					match = true;
 					break;
 				}
@@ -2766,8 +2774,9 @@ add_child_rel_equivalences(PlannerInfo *root,
 	while ((i = bms_next_member(parent_rel->eclass_indexes, i)) >= 0)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
-		Bitmapset		 *matching_ems;
-		int			j;
+		EquivalenceMember *cur_em;
+		EquivalenceMemberIterator it;
+		int					final_member;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2784,13 +2793,18 @@ add_child_rel_equivalences(PlannerInfo *root,
 		 * Looping over matching_ems means we only loop over existing members,
 		 * not any newly added ones.
 		 */
-		matching_ems = get_ecmember_indexes(root, cur_ec, top_parent_relids, false, false);
+		get_ecmember_indexes_iterator(&it, root, cur_ec, top_parent_relids, false, false, false);
 
-		j = -1;
-		while ((j = bms_next_member(matching_ems, j)) >= 0)
-		{
-			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember, root->eq_members, j);
+		/*
+		 * TODO: To skip newly added EMs, we get final member's index.
+		 * However, touching inside of EquivalenceMemberIterator is not ideal
+		 * solution.
+		 */
+		final_member = bms_prev_member(cur_ec->ec_member_indexes, -1);
 
+		while ((cur_em = get_next_matching_member(root, &it)) != NULL
+				&& it.position <= final_member)
+		{
 			Assert(!cur_em->em_is_const);
 			Assert(!cur_em->em_is_child);
 			Assert(bms_overlap(cur_em->em_relids, top_parent_relids));
@@ -2893,8 +2907,9 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 	while ((i = bms_next_member(matching_ecs, i)) >= 0)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
-		Bitmapset		 *matching_ems;
-		int			j;
+		EquivalenceMember *cur_em;
+		EquivalenceMemberIterator it;
+		int					final_member;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2911,13 +2926,18 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 		 * Looping over matching_ems means we only loop over existing members,
 		 * not any newly added ones.
 		 */
-		matching_ems = get_ecmember_indexes(root, cur_ec, top_parent_relids, false, false);
+		get_ecmember_indexes_iterator(&it, root, cur_ec, top_parent_relids, false, false, false);
 
-		j = -1;
-		while ((j = bms_next_member(matching_ems, j)) >= 0)
-		{
-			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember, root->eq_members, j);
+		/*
+		 * TODO: To skip newly added EMs, we get final member's index.
+		 * However, touching inside of EquivalenceMemberIterator is not ideal
+		 * solution.
+		 */
+		final_member = bms_prev_member(cur_ec->ec_member_indexes, -1);
 
+		while ((cur_em = get_next_matching_member(root, &it)) != NULL
+				&& it.position <= final_member)
+		{
 			Assert(!cur_em->em_is_const);
 			Assert(!cur_em->em_is_child);
 			Assert(bms_overlap(cur_em->em_relids, top_parent_relids));
@@ -3038,7 +3058,7 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		EquivalenceMember *cur_em;
-		Bitmapset		  *matching_ems;
+		EquivalenceMemberIterator it;
 		int					j;
 
 		/* Sanity check eclass_indexes only contain ECs for rel */
@@ -3062,13 +3082,9 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 		 * corner cases, so for now we live with just reporting the first
 		 * match.  See also get_eclass_for_sort_expr.)
 		 */
-		matching_ems = get_ecmember_indexes(root, cur_ec, rel->relids, true, false);
-		cur_em = NULL;
-		j = -1;
-		while ((j = bms_next_member(matching_ems, j)) >= 0)
+		get_ecmember_indexes_iterator(&it, root, cur_ec, rel->relids, true, false, true);
+		while ((cur_em = get_next_matching_member(root, &it)) != NULL)
 		{
-			cur_em = list_nth_node(EquivalenceMember, root->eq_members, j);
-
 			if (bms_equal(cur_em->em_relids, rel->relids) &&
 				callback(root, rel, cur_ec, cur_em, callback_arg))
 				break;
@@ -3477,6 +3493,51 @@ get_ecmember_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids,
 	return matching_ems;
 }
 
+/*
+ * get_ecmember_indexes_iterator
+ *		Initializes an EquivalenceMemberIterator for the given arguments so
+ *		that the iterator enumerates the EquivalenceMembers obtained by
+ *		get_ecmember_indexes().
+ *		If the caller_needs_recheck is true, the EquivalenceMembers to be
+ *		iterated may have false positives, so the caller must check the
+ *		desired condition.
+ */
+void
+get_ecmember_indexes_iterator(EquivalenceMemberIterator *it,
+							  PlannerInfo *root,
+							  EquivalenceClass *ec,
+							  Relids relids,
+							  bool with_children,
+							  bool with_norel_members,
+							  bool caller_needs_recheck)
+{
+	it->relids = relids;
+	it->position = -1;
+
+	if (root->simple_rel_array_size > EC_MEMBER_ITERATOR_THRESHOLD)
+	{
+		/* We use index */
+		it->check_on_iterate = false;
+		it->index = get_ecmember_indexes(root, ec, relids,
+										 with_children,
+										 with_norel_members);
+	}
+	else
+	{
+		Bitmapset *index;
+
+		/* We don't use index, perform linear search instead */
+		it->check_on_iterate = !caller_needs_recheck;
+
+		index =
+			with_children ? ec->ec_member_indexes : ec->ec_nonchild_indexes;
+		if (!with_norel_members)
+			index = bms_difference(index, ec->ec_norel_indexes);
+
+		it->index = index;
+	}
+}
+
 /*
  * get_ecmember_indexes_strict
  *		Returns a Bitmapset with indexes into root->eq_members for all
@@ -3524,6 +3585,51 @@ get_ecmember_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 	return matching_ems;
 }
 
+/*
+ * get_ecmember_indexes_strict_iterator
+ *		Initializes an EquivalenceMemberIterator for the given arguments so
+ *		that the iterator enumerates the EquivalenceMembers obtained by
+ *		get_ecmember_indexes_strict().
+ *		If the caller_needs_recheck is true, the EquivalenceMembers to be
+ *		iterated may have false positives, so the caller must check the
+ *		desired condition.
+ */
+void
+get_ecmember_indexes_strict_iterator(EquivalenceMemberStrictIterator *it,
+									 PlannerInfo *root,
+									 EquivalenceClass *ec,
+									 Relids relids,
+									 bool with_children,
+									 bool with_norel_members,
+									 bool caller_needs_recheck)
+{
+	it->relids = relids;
+	it->position = -1;
+
+	if (root->simple_rel_array_size > EC_MEMBER_ITERATOR_THRESHOLD)
+	{
+		/* We use index */
+		it->check_on_iterate = false;
+		it->index = get_ecmember_indexes_strict(root, ec, relids,
+												with_children,
+												with_norel_members);
+	}
+	else
+	{
+		Bitmapset *index;
+
+		/* We don't use index, perform linear search instead */
+		it->check_on_iterate = !caller_needs_recheck;
+
+		index =
+			with_children ? ec->ec_member_indexes : ec->ec_nonchild_indexes;
+		if (!with_norel_members)
+			index = bms_difference(index, ec->ec_norel_indexes);
+
+		it->index = index;
+	}
+}
+
 /*
  * get_ec_source_indexes
  *		Returns a Bitmapset with indexes into root->eq_sources for all
@@ -3569,6 +3675,40 @@ get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
 	return matching_es;
 }
 
+/*
+ * get_ec_source_indexes_iterator
+ *		Initializes an RestrictInfoIterator for the given arguments so
+ *		that the iterator enumerates the RestrictInfos obtained by
+ *		get_ec_source_indexes().
+ *		If the caller_needs_recheck is true, the RestrictInfos to be
+ *		iterated may have false positives, so the caller must check the
+ *		desired condition.
+ */
+void
+get_ec_source_indexes_iterator(RestrictInfoIterator *it,
+							   PlannerInfo *root,
+							   EquivalenceClass *ec,
+							   Relids relids,
+							   bool caller_needs_recheck)
+{
+	it->relids = relids;
+	it->position = -1;
+	it->members = root->eq_sources;
+
+	if (root->simple_rel_array_size > EC_MEMBER_ITERATOR_THRESHOLD)
+	{
+		/* We use index */
+		it->check_on_iterate = false;
+		it->indexes = get_ec_source_indexes(root, ec, relids);
+	}
+	else
+	{
+		/* We don't use index, perform linear search instead */
+		it->check_on_iterate = !caller_needs_recheck;
+		it->indexes = ec->ec_source_indexes;
+	}
+}
+
 /*
  * get_ec_source_indexes_strict
  *		Returns a Bitmapset with indexes into root->eq_sources for all
@@ -3614,6 +3754,40 @@ get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec, Relids rel
 	return matching_es;
 }
 
+/*
+ * get_ec_source_indexes_strict_iterator
+ *		Initializes an RestrictInfoIterator for the given arguments so
+ *		that the iterator enumerates the RestrictInfos obtained by
+ *		get_ec_source_indexes_strict().
+ *		If the caller_needs_recheck is true, the RestrictInfos to be
+ *		iterated may have false positives, so the caller must check the
+ *		desired condition.
+ */
+void
+get_ec_source_indexes_strict_iterator(RestrictInfoStrictIterator *it,
+									  PlannerInfo *root,
+									  EquivalenceClass *ec,
+									  Relids relids,
+									  bool caller_needs_recheck)
+{
+	it->relids = relids;
+	it->position = -1;
+	it->members = root->eq_sources;
+
+	if (root->simple_rel_array_size > EC_MEMBER_ITERATOR_THRESHOLD)
+	{
+		/* We use index */
+		it->check_on_iterate = false;
+		it->indexes = get_ec_source_indexes_strict(root, ec, relids);
+	}
+	else
+	{
+		/* We don't use index, perform linear search instead */
+		it->check_on_iterate = !caller_needs_recheck;
+		it->indexes = ec->ec_source_indexes;
+	}
+}
+
 /*
  * get_ec_derive_indexes
  *		Returns a Bitmapset with indexes into root->eq_derives for all
@@ -3659,6 +3833,40 @@ get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
 	return matching_eds;
 }
 
+/*
+ * get_ec_derive_indexes_iterator
+ *		Initializes an RestrictInfoIterator for the given arguments so
+ *		that the iterator enumerates the RestrictInfos obtained by
+ *		get_ec_derive_indexes().
+ *		If the caller_needs_recheck is true, the RestrictInfos to be
+ *		iterated may have false positives, so the caller must check the
+ *		desired condition.
+ */
+void
+get_ec_derive_indexes_iterator(RestrictInfoIterator *it,
+							   PlannerInfo *root,
+							   EquivalenceClass *ec,
+							   Relids relids,
+							   bool caller_needs_recheck)
+{
+	it->relids = relids;
+	it->position = -1;
+	it->members = root->eq_derives;
+
+	if (root->simple_rel_array_size > EC_MEMBER_ITERATOR_THRESHOLD)
+	{
+		/* We use index */
+		it->check_on_iterate = false;
+		it->indexes = get_ec_derive_indexes(root, ec, relids);
+	}
+	else
+	{
+		/* We don't use index, perform linear search instead */
+		it->check_on_iterate = !caller_needs_recheck;
+		it->indexes = ec->ec_derive_indexes;
+	}
+}
+
 /*
  * get_ec_derive_indexes_strict
  *		Returns a Bitmapset with indexes into root->eq_derives for all
@@ -3703,3 +3911,37 @@ get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec, Relids rel
 
 	return matching_eds;
 }
+
+/*
+ * get_ec_derive_indexes_strict_iterator
+ *		Initializes an RestrictInfoIterator for the given arguments so
+ *		that the iterator enumerates the RestrictInfos obtained by
+ *		get_ec_derive_indexes_strict().
+ *		If the caller_needs_recheck is true, the RestrictInfos to be
+ *		iterated may have false positives, so the caller must check the
+ *		desired condition.
+ */
+void
+get_ec_derive_indexes_strict_iterator(RestrictInfoStrictIterator *it,
+									  PlannerInfo *root,
+									  EquivalenceClass *ec,
+									  Relids relids,
+									  bool caller_needs_recheck)
+{
+	it->relids = relids;
+	it->position = -1;
+	it->members = root->eq_derives;
+
+	if (root->simple_rel_array_size > EC_MEMBER_ITERATOR_THRESHOLD)
+	{
+		/* We use index */
+		it->check_on_iterate = false;
+		it->indexes = get_ec_derive_indexes_strict(root, ec, relids);
+	}
+	else
+	{
+		/* We don't use index, perform linear search instead */
+		it->check_on_iterate = !caller_needs_recheck;
+		it->indexes = ec->ec_derive_indexes;
+	}
+}
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index bf6389fee4..64306990fa 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -1378,6 +1378,38 @@ typedef struct EquivalenceMember
 	Oid			em_datatype;	/* the "nominal type" used by the opfamily */
 } EquivalenceMember;
 
+typedef struct EquivalenceMemberIterator
+{
+	/* Do we have to check the condition on iterate? */
+	bool		check_on_iterate;
+
+	/* Last found index of the Bitmapset 'index' or -1 */
+	int			position;
+
+	/* Relids for checking the condition */
+	Relids		relids;
+
+	/* Indexes of the EquivalenceMembers to be iterated */
+	Bitmapset  *index;
+} EquivalenceMemberIterator;
+
+typedef struct EquivalenceMemberStrictIterator
+{
+	/* Do we have to check the condition on iterate? */
+	bool		check_on_iterate;
+
+	/* Last found index of the Bitmapset 'index' or -1 */
+	int			position;
+
+	/* Relids for checking the condition */
+	Relids		relids;
+
+	/* Indexes of the EquivalenceMembers to be iterated */
+	Bitmapset  *index;
+} EquivalenceMemberStrictIterator;
+
+#define EC_MEMBER_ITERATOR_THRESHOLD 32
+
 /*
  * PathKeys
  *
@@ -2607,6 +2639,42 @@ typedef struct RestrictInfo
 	((rinfo)->is_pushed_down || \
 	 !bms_is_subset((rinfo)->required_relids, joinrelids))
 
+typedef struct RestrictInfoIterator
+{
+	/* Do we have to check the condition on iterate? */
+	bool		check_on_iterate;
+
+	/* Last found index of the Bitmapset 'index' or -1 */
+	int			position;
+
+	/* Relids for checking the condition */
+	Relids		relids;
+
+	/* Indexes of the RestrictInfos to be iterated */
+	Bitmapset  *indexes;
+
+	/* RestrictInfos that the 'indexes' point to */
+	List	   *members;
+} RestrictInfoIterator;
+
+typedef struct RestrictInfoStrictIterator
+{
+	/* Do we have to check the condition on iterate? */
+	bool		check_on_iterate;
+
+	/* Last found index of the Bitmapset 'index' or -1 */
+	int			position;
+
+	/* Relids for checking the condition */
+	Relids		relids;
+
+	/* Indexes of the RestrictInfos to be iterated */
+	Bitmapset  *indexes;
+
+	/* RestrictInfos that the 'indexes' point to */
+	List	   *members;
+} RestrictInfoStrictIterator;
+
 /*
  * Since mergejoinscansel() is a relatively expensive function, and would
  * otherwise be invoked many times while planning a large join tree,
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index f6835f80f5..d78dadb074 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -194,23 +194,169 @@ extern Bitmapset *get_ecmember_indexes(PlannerInfo *root,
 										   Relids relids,
 										   bool with_children,
 										   bool with_norel_members);
+extern void get_ecmember_indexes_iterator(EquivalenceMemberIterator *it,
+										  PlannerInfo *root,
+										  EquivalenceClass *ec,
+										  Relids relids,
+										  bool with_children,
+										  bool with_norel_members,
+										  bool caller_needs_recheck);
 extern Bitmapset *get_ecmember_indexes_strict(PlannerInfo *root,
 											  EquivalenceClass *ec,
 											  Relids relids,
 											  bool with_children,
 											  bool with_norel_members);
+extern void get_ecmember_indexes_strict_iterator(EquivalenceMemberStrictIterator *it,
+												 PlannerInfo *root,
+												 EquivalenceClass *ec,
+												 Relids relids,
+												 bool with_children,
+												 bool with_norel_members,
+												 bool caller_needs_recheck);
 extern Bitmapset *get_ec_source_indexes(PlannerInfo *root,
 										EquivalenceClass *ec,
 										Relids relids);
+extern void get_ec_source_indexes_iterator(RestrictInfoIterator *it,
+										   PlannerInfo *root,
+										   EquivalenceClass *ec,
+										   Relids relids,
+										   bool caller_needs_recheck);
 extern Bitmapset *get_ec_source_indexes_strict(PlannerInfo *root,
 											   EquivalenceClass *ec,
 											   Relids relids);
+extern void get_ec_source_indexes_strict_iterator(RestrictInfoStrictIterator *it,
+												  PlannerInfo *root,
+												  EquivalenceClass *ec,
+												  Relids relids,
+												  bool caller_needs_recheck);
 extern Bitmapset *get_ec_derive_indexes(PlannerInfo *root,
 										EquivalenceClass *ec,
 										Relids relids);
+extern void get_ec_derive_indexes_iterator(RestrictInfoIterator *it,
+										   PlannerInfo *root,
+										   EquivalenceClass *ec,
+										   Relids relids,
+										   bool caller_needs_recheck);
 extern Bitmapset *get_ec_derive_indexes_strict(PlannerInfo *root,
-										EquivalenceClass *ec,
-										Relids relids);
+											   EquivalenceClass *ec,
+											   Relids relids);
+extern void get_ec_derive_indexes_strict_iterator(RestrictInfoStrictIterator *it,
+												  PlannerInfo *root,
+												  EquivalenceClass *ec,
+												  Relids relids,
+												  bool caller_needs_recheck);
+
+/*
+ * Returns the next EquivalenceMember. If the iteration was done, returns NULL.
+ */
+static inline EquivalenceMember *
+get_next_matching_member(PlannerInfo *root, EquivalenceMemberIterator *it)
+{
+	if (!it->check_on_iterate)
+	{
+		if ((it->position = bms_next_member(it->index, it->position)) >= 0)
+			return list_nth_node(EquivalenceMember, root->eq_members, it->position);
+	}
+	else
+	{
+		while ((it->position = bms_next_member(it->index, it->position)) >= 0)
+		{
+			EquivalenceMember *member =
+				list_nth_node(EquivalenceMember, root->eq_members, it->position);
+
+			if (bms_overlap(it->relids, member->em_relids))
+			{
+				return member;
+			}
+		}
+	}
+
+	return NULL;
+}
+
+/*
+ * Returns the next EquivalenceMember. If the iteration was done, returns NULL.
+ */
+static inline EquivalenceMember *
+get_next_matching_member_strict(PlannerInfo *root, EquivalenceMemberStrictIterator *it)
+{
+	if (!it->check_on_iterate)
+	{
+		if ((it->position = bms_next_member(it->index, it->position)) >= 0)
+			return list_nth_node(EquivalenceMember, root->eq_members, it->position);
+	}
+	else
+	{
+		while ((it->position = bms_next_member(it->index, it->position)) >= 0)
+		{
+			EquivalenceMember *member =
+				list_nth_node(EquivalenceMember, root->eq_members, it->position);
+
+			if (bms_is_subset(it->relids, member->em_relids))
+			{
+				return member;
+			}
+		}
+	}
+
+	return NULL;
+}
+
+/*
+ * Returns the next RestrictInfo. If the iteration was done, returns NULL.
+ */
+static inline RestrictInfo *
+get_next_matching_rinfo(PlannerInfo *root, RestrictInfoIterator *it)
+{
+	if (!it->check_on_iterate)
+	{
+		if ((it->position = bms_next_member(it->indexes, it->position)) >= 0)
+			return list_nth_node(RestrictInfo, it->members, it->position);
+	}
+	else
+	{
+		while ((it->position = bms_next_member(it->indexes, it->position)) >= 0)
+		{
+			RestrictInfo *member =
+				list_nth_node(RestrictInfo, it->members, it->position);
+
+			if (bms_overlap(it->relids, member->clause_relids))
+			{
+				return member;
+			}
+		}
+	}
+
+	return NULL;
+}
+
+/*
+ * Returns the next RestrictInfo. If the iteration was done, returns NULL.
+ */
+static inline RestrictInfo *
+get_next_matching_rinfo_strict(PlannerInfo *root, RestrictInfoStrictIterator *it)
+{
+	if (!it->check_on_iterate)
+	{
+		if ((it->position = bms_next_member(it->indexes, it->position)) >= 0)
+			return list_nth_node(RestrictInfo, it->members, it->position);
+	}
+	else
+	{
+		while ((it->position = bms_next_member(it->indexes, it->position)) >= 0)
+		{
+			RestrictInfo *member =
+				list_nth_node(RestrictInfo, it->members, it->position);
+
+			if (bms_is_subset(it->relids, member->clause_relids))
+			{
+				return member;
+			}
+		}
+	}
+
+	return NULL;
+}
 
 /*
  * pathkeys.c
-- 
2.35.3.windows.1